Skip to content

Commit 9831ce2

Browse files
Add an option to bundle NPM packages while installing
1 parent b42ae33 commit 9831ce2

File tree

6 files changed

+474
-45
lines changed

6 files changed

+474
-45
lines changed

npm/private/lifecycle/lifecycle-hooks.js

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ const path = require('path')
55
const { safeReadPackageJsonFromDir } = require('@pnpm/read-package-json')
66
const { runLifecycleHook } = require('@pnpm/lifecycle')
77

8+
const {rollup} = require('rollup');
9+
const {dts} = require('rollup-plugin-dts');
10+
const commonjs = require('@rollup/plugin-commonjs');
11+
const { nodeResolve } = require('@rollup/plugin-node-resolve');
12+
813
async function mkdirp(p) {
914
if (p && !fs.existsSync(p)) {
1015
await mkdirp(path.dirname(p))
@@ -128,6 +133,89 @@ function isWindows() {
128133
return os.platform() === 'win32'
129134
}
130135

136+
137+
async function optimizePackage(destDir) {
138+
// We first copy the package to a temporary directory so that we don't have `node_modules`
139+
// in our name. Otherwise, `dts` will refuse to do anthing, since `respectExternals` defaults to false.
140+
// Setting that to true will inline _all_ externals (including dependencies), which
141+
// can break typechecking. Better to bundle the dependencies on their own.
142+
const tempDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'aspect_rules_js_'));
143+
await fs.promises.cp(destDir, tempDir, {recursive: true, force: true});
144+
145+
const packageJsonText = await fs.promises.readFile(path.join(tempDir, 'package.json'));
146+
const packageJson = JSON.parse(packageJsonText);
147+
148+
const typesMain = path.join(tempDir, packageJson.types);
149+
const cjsMain = path.join(tempDir, packageJson.main);
150+
const esmMain = path.join(tempDir, packageJson.module);
151+
152+
async function bundleDts(input) {
153+
const result = await rollup({
154+
input,
155+
plugins: [dts()],
156+
});
157+
await result.write({
158+
file: input,
159+
format: 'esm',
160+
});
161+
}
162+
163+
async function bundle(input, format, plugins) {
164+
const result = await rollup({
165+
input,
166+
output: {
167+
file: input,
168+
format,
169+
},
170+
plugins,
171+
external: [/package\.json$/],
172+
});
173+
await result.write({
174+
file: input,
175+
format,
176+
});
177+
}
178+
179+
const bundlePromises = [];
180+
typesMain && bundlePromises.push(bundleDts(typesMain));
181+
cjsMain && bundlePromises.push(bundle(cjsMain, 'cjs', [commonjs()]));
182+
esmMain && bundlePromises.push(bundle(esmMain, 'esm', [nodeResolve()]));
183+
184+
// Would be faster to use esbuild, but we cannot bundle esbuild itself :/
185+
/*cjsMain && bundlePromises.push(esbuild.build({
186+
entryPoints: [cjsMain],
187+
bundle: true,
188+
outfile: cjsMain,
189+
platform: 'node',
190+
format: 'cjs',
191+
}));
192+
esmMain && bundlePromises.push(esbuild.build({
193+
entryPoints: [esmMain],
194+
bundle: true,
195+
outfile: esmMain,
196+
platform: 'node',
197+
format: 'esm',
198+
}));*/
199+
200+
await Promise.all(bundlePromises);
201+
202+
await fs.promises.rm(destDir, {recursive: true, force: true});
203+
204+
async function rename(tmpPath, realPath) {
205+
const realDir = path.dirname(realPath);
206+
await fs.promises.mkdir(realDir, {recursive: true});
207+
await fs.promises.cp(tmpPath, realPath);
208+
}
209+
210+
const emitPromises = [];
211+
typesMain && emitPromises.push(rename(typesMain, path.join(destDir, packageJson.types)));
212+
cjsMain && emitPromises.push(rename(cjsMain, path.join(destDir, packageJson.main)));
213+
esmMain && emitPromises.push(rename(esmMain, path.join(destDir, packageJson.module)));
214+
await Promise.all(emitPromises);
215+
216+
await fs.promises.writeFile(path.join(destDir, 'package.json'), packageJsonText);
217+
}
218+
131219
async function main(args) {
132220
if (args.length < 3) {
133221
console.error(
@@ -260,6 +348,10 @@ async function main(args) {
260348
// Run user specified custom postinstall hook
261349
await runLifecycleHook('custom_postinstall', rulesJsJson, opts)
262350
}
351+
352+
if (rulesJsJson.optimize_package) {
353+
await optimizePackage(outputDir);
354+
}
263355
}
264356

265357
// Copy contents of a package dir to a destination dir (without copying the package dir itself)

npm/private/lifecycle/min/index.min.js

Lines changed: 116 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)