-
Notifications
You must be signed in to change notification settings - Fork 343
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
chore: use rollup instead of tsup for a cleaner dist #255
Changes from 13 commits
4ae8086
7d4bdf7
24f9bc1
1d992c8
192dde0
62a5dcb
a767272
6cebc31
529ad55
9e7ecfb
3fea9d3
2e36b06
62ea66f
febc2d9
75d9a0b
a07a3ef
76aae2e
459b6e7
0142e9d
fd84a98
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,6 +28,7 @@ | |
}, | ||
"files": [ | ||
"dist", | ||
"umd", | ||
"*.d.ts" | ||
], | ||
"publishConfig": { | ||
|
@@ -136,6 +137,8 @@ | |
"@babel/preset-typescript": "^7.24.1", | ||
"@changesets/changelog-github": "^0.5.0", | ||
"@changesets/cli": "^2.27.1", | ||
"@rollup/plugin-terser": "^0.4.4", | ||
"@rollup/plugin-typescript": "^11.1.6", | ||
"@types/babel__core": "^7", | ||
"@types/babel__preset-env": "^7", | ||
"@types/broken-link-checker": "^0", | ||
|
@@ -148,14 +151,16 @@ | |
"eslint-config-prettier": "^8.5.0", | ||
"eslint-plugin-jsdoc": "^48.5.0", | ||
"prettier": "^3.2.5", | ||
"tsup": "^8.1.0", | ||
"rollup": "^4.19.0", | ||
"rollup-plugin-dts": "^6.1.1", | ||
"tslib": "^2.6.3", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The TypeScript compiler (used via |
||
"typescript": "^5.4.5", | ||
"vitest": "^1.5.2" | ||
}, | ||
"sideEffects": false, | ||
"scripts": { | ||
"prepack": "yarn build", | ||
"build": "tsup && ./.scripts/postbuild.sh", | ||
"build": "rollup -c rollup.config.mjs && ./.scripts/postbuild.sh", | ||
"test": "vitest run --coverage --typecheck", | ||
"bench": "vitest bench", | ||
"lint": "eslint ./src --ext .ts", | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,172 @@ | ||||||||||||||||
// @ts-check | ||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the Rollup configuration. Rollup uses Node to load this module, and supports We can still use TypeScript types (which is quite useful for autocompletion) using I used ESM, but I’m happy to convert to CJS if that's preferred. |
||||||||||||||||
|
||||||||||||||||
import fs from 'node:fs'; | ||||||||||||||||
import { createRequire } from 'node:module'; | ||||||||||||||||
import path from 'node:path'; | ||||||||||||||||
import terserPlugin from '@rollup/plugin-terser'; | ||||||||||||||||
import tsPlugin from '@rollup/plugin-typescript'; | ||||||||||||||||
import dtsPlugin from 'rollup-plugin-dts'; | ||||||||||||||||
Comment on lines
+8
to
+9
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We're using const config = {
// …
plugins: [
tsPlugin({
compilerOptions: {
declaration: true,
declarationDir: 'dist',
}
],
{
output: 'esm',
dir: 'dist',
entryFileNames: '[name].mjs',
}
}); Then we get this output:
with the declaration file having a // dist/index.d.ts
export * from './array/index.ts';
export * from './error/index.ts';
export * from './function/index.ts';
export * from './math/index.ts';
export * from './object/index.ts';
export * from './predicate/index.ts';
export * from './promise/index.ts';
export * from './string/index.ts'; Finally, these declaration files are emitted by the TypeScript compiler directly to disk. Which means that we cannot use Rollup configuration options, or even a simple custom output plugin, to:
I was stuck on this problem for a long while, then found So in total we are doing 4 different Rollup builds:
There might be a better solution than using |
||||||||||||||||
|
||||||||||||||||
/** | ||||||||||||||||
* @type {{ | ||||||||||||||||
* exports: Record<string, string>; | ||||||||||||||||
* publishConfig: { browser: string }; | ||||||||||||||||
* }} | ||||||||||||||||
*/ | ||||||||||||||||
const packageJson = createRequire(import.meta.url)('./package.json'); | ||||||||||||||||
|
||||||||||||||||
const testPatterns = ['**/*.bench.ts', '**/*.spec.ts', '**/*.test.ts']; | ||||||||||||||||
|
||||||||||||||||
export default () => { | ||||||||||||||||
clearDir('dist'); | ||||||||||||||||
clearDir('umd'); | ||||||||||||||||
|
||||||||||||||||
const entrypoints = Object.values(packageJson.exports).filter(f => /^(\.\/)?src\//.test(f) && f.endsWith('.ts')); | ||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After a bunch of testing: using the paths from
Another approach is to use something like |
||||||||||||||||
|
||||||||||||||||
return [ | ||||||||||||||||
libBuildOptions({ | ||||||||||||||||
format: 'esm', | ||||||||||||||||
extension: 'mjs', | ||||||||||||||||
entrypoints, | ||||||||||||||||
outDir: 'dist', | ||||||||||||||||
}), | ||||||||||||||||
libBuildOptions({ | ||||||||||||||||
format: 'cjs', | ||||||||||||||||
extension: 'js', | ||||||||||||||||
entrypoints, | ||||||||||||||||
outDir: 'dist', | ||||||||||||||||
}), | ||||||||||||||||
declarationOptions({ | ||||||||||||||||
entrypoints, | ||||||||||||||||
outDir: 'dist', | ||||||||||||||||
}), | ||||||||||||||||
browserBuildConfig({ | ||||||||||||||||
inputFile: './src/compat/index.ts', | ||||||||||||||||
outFile: packageJson.publishConfig.browser, | ||||||||||||||||
name: '_', | ||||||||||||||||
}), | ||||||||||||||||
Comment on lines
+48
to
+52
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can use Rollup to generate the Here, the var _ = function (t) {
"use strict";
// ... implementation, sets t.functionName = ...
}({}); Note that the current window._ = toolkit; This
Solutions include:
|
||||||||||||||||
]; | ||||||||||||||||
}; | ||||||||||||||||
|
||||||||||||||||
/** | ||||||||||||||||
* @type {(options: { | ||||||||||||||||
* entrypoints: string[]; | ||||||||||||||||
* format: 'esm' | 'cjs'; | ||||||||||||||||
* extension: 'js' | 'cjs' | 'mjs'; | ||||||||||||||||
* outDir: string; | ||||||||||||||||
* }) => import('rollup').RollupOptions} | ||||||||||||||||
*/ | ||||||||||||||||
function libBuildOptions({ entrypoints, extension, format, outDir }) { | ||||||||||||||||
const isESM = format === 'esm'; | ||||||||||||||||
|
||||||||||||||||
return { | ||||||||||||||||
input: mapInputs(entrypoints), | ||||||||||||||||
plugins: [ | ||||||||||||||||
tsPlugin({ | ||||||||||||||||
exclude: [...testPatterns], | ||||||||||||||||
compilerOptions: { | ||||||||||||||||
sourceMap: true, | ||||||||||||||||
inlineSources: true, | ||||||||||||||||
declaration: false, | ||||||||||||||||
removeComments: true, | ||||||||||||||||
}, | ||||||||||||||||
}), | ||||||||||||||||
], | ||||||||||||||||
output: { | ||||||||||||||||
format, | ||||||||||||||||
dir: outDir, | ||||||||||||||||
...fileNames(extension), | ||||||||||||||||
// Using preserveModules disables bundling and the creation of chunks, | ||||||||||||||||
// leading to a result that is a mirror of the input module graph. | ||||||||||||||||
preserveModules: isESM, | ||||||||||||||||
sourcemap: true, | ||||||||||||||||
generatedCode: 'es2015', | ||||||||||||||||
// Hoisting transitive imports adds bare imports in modules, | ||||||||||||||||
// which can make imports by JS runtimes slightly faster, | ||||||||||||||||
// but makes the generated code harder to follow. | ||||||||||||||||
hoistTransitiveImports: false, | ||||||||||||||||
}, | ||||||||||||||||
}; | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
/** | ||||||||||||||||
* @type {(options: {inputFile: string; outFile: string; name: string}) => import('rollup').RollupOptions} | ||||||||||||||||
*/ | ||||||||||||||||
function browserBuildConfig({ inputFile, outFile, name }) { | ||||||||||||||||
return { | ||||||||||||||||
input: inputFile, | ||||||||||||||||
plugins: [ | ||||||||||||||||
tsPlugin({ | ||||||||||||||||
exclude: [...testPatterns], | ||||||||||||||||
compilerOptions: { | ||||||||||||||||
sourceMap: true, | ||||||||||||||||
inlineSources: true, | ||||||||||||||||
removeComments: true, | ||||||||||||||||
declaration: false, | ||||||||||||||||
}, | ||||||||||||||||
}), | ||||||||||||||||
], | ||||||||||||||||
output: { | ||||||||||||||||
plugins: [terserPlugin()], | ||||||||||||||||
format: 'iife', | ||||||||||||||||
name, | ||||||||||||||||
file: outFile, | ||||||||||||||||
sourcemap: true, | ||||||||||||||||
generatedCode: 'es2015', | ||||||||||||||||
}, | ||||||||||||||||
}; | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
/** | ||||||||||||||||
* @type {(options: {entrypoints: string[]; outDir: string}) => import('rollup').RollupOptions} | ||||||||||||||||
*/ | ||||||||||||||||
function declarationOptions({ entrypoints, outDir }) { | ||||||||||||||||
return { | ||||||||||||||||
plugins: [dtsPlugin()], | ||||||||||||||||
input: mapInputs(entrypoints), | ||||||||||||||||
output: [ | ||||||||||||||||
{ | ||||||||||||||||
format: 'esm', | ||||||||||||||||
dir: outDir, | ||||||||||||||||
generatedCode: 'es2015', | ||||||||||||||||
...fileNames('d.mts'), | ||||||||||||||||
preserveModules: true, | ||||||||||||||||
preserveModulesRoot: 'src', | ||||||||||||||||
}, | ||||||||||||||||
{ | ||||||||||||||||
format: 'cjs', | ||||||||||||||||
dir: outDir, | ||||||||||||||||
generatedCode: 'es2015', | ||||||||||||||||
...fileNames('d.ts'), | ||||||||||||||||
preserveModules: true, | ||||||||||||||||
preserveModulesRoot: 'src', | ||||||||||||||||
}, | ||||||||||||||||
], | ||||||||||||||||
}; | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
/** @type {(srcFiles: string[]) => Record<string, string>} */ | ||||||||||||||||
function mapInputs(srcFiles) { | ||||||||||||||||
return Object.fromEntries( | ||||||||||||||||
srcFiles.map(file => [ | ||||||||||||||||
file.replace(/^(\.\/)?src\//, '').replace(/\.[cm]?(js|ts)$/, ''), | ||||||||||||||||
path.join(import.meta.dirname, file), | ||||||||||||||||
]) | ||||||||||||||||
); | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
function fileNames(extension = 'js') { | ||||||||||||||||
return { | ||||||||||||||||
entryFileNames: `[name].${extension}`, | ||||||||||||||||
chunkFileNames: `_chunk/[name]-[hash:6].${extension}`, | ||||||||||||||||
}; | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
/** @type {(dir: string) => void} */ | ||||||||||||||||
function clearDir(dir) { | ||||||||||||||||
const dirPath = path.join(import.meta.dirname, dir); | ||||||||||||||||
if (dir && fs.existsSync(dirPath)) { | ||||||||||||||||
fs.rmSync(dirPath, { recursive: true, force: true }); | ||||||||||||||||
console.log(`cleared: ${dir}`); | ||||||||||||||||
} | ||||||||||||||||
} |
This file was deleted.
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This fixes publishing the
umd/browser.global.js.map
sourcemap.The
package.json#browser
field will prompt npm/yarn to includeumd/browser.global.js
in the package, but the related sourcemap is ignored unless explicitly included.See also: #284
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your catch!