Skip to content

Commit

Permalink
WIP: add library bundler
Browse files Browse the repository at this point in the history
  • Loading branch information
devongovett committed Jan 14, 2024
1 parent 874dddf commit 597c63b
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 4 deletions.
26 changes: 26 additions & 0 deletions packages/bundlers/library/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "@parcel/bundler-library",
"version": "2.11.0",
"license": "MIT",
"publishConfig": {
"access": "public"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
},
"repository": {
"type": "git",
"url": "https://github.com/parcel-bundler/parcel.git"
},
"main": "lib/LibraryBundler.js",
"source": "src/LibraryBundler.js",
"engines": {
"node": ">= 12.0.0",
"parcel": "^2.11.0"
},
"dependencies": {
"@parcel/plugin": "2.11.0",
"nullthrows": "^1.1.1"
}
}
41 changes: 41 additions & 0 deletions packages/bundlers/library/src/LibraryBundler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// @flow strict-local
import {Bundler} from '@parcel/plugin';
import nullthrows from 'nullthrows';

export default (new Bundler({
bundle({bundleGraph}) {
let bundles = new Map();
bundleGraph.traverse((node, context) => {
if (node.type === 'dependency') {
let dependency = node.value;
let parentAsset = bundleGraph.getAssetWithDependency(dependency);
let assets = bundleGraph.getDependencyAssets(dependency);

// Create a separate bundle group/bundle for each asset.
for (let asset of assets) {
let target = nullthrows(dependency.target ?? context);
let bundleGroup = bundleGraph.createBundleGroup(dependency, target);
let bundle = bundleGraph.createBundle({
entryAsset: asset,
needsStableName: !parentAsset,
target,
});
bundleGraph.addAssetToBundle(asset, bundle);
bundleGraph.addBundleToBundleGroup(bundle, bundleGroup);

// Reference the parent bundle so we create dependencies between them.
let parentBundle = parentAsset && bundles.get(parentAsset);
if (parentBundle) {
bundleGraph.createBundleReference(parentBundle, bundle);
}
bundles.set(asset, bundle);
}

if (dependency.target) {
return dependency.target;
}
}
});
},
optimize() {},
}): Bundler);
Empty file.
92 changes: 92 additions & 0 deletions packages/core/integration-tests/test/library-bundler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// @flow strict-local
import assert from 'assert';
import path from 'path';
import {
bundle,
run,
overlayFS,
outputFS,
fsFixture,
assertBundles,
} from '@parcel/test-utils';

describe('library bundler', function () {
let count = 0;
let dir;
beforeEach(async () => {
dir = path.join(__dirname, 'libraries', '' + ++count);
await overlayFS.mkdirp(dir);
});

after(async () => {
await overlayFS.rimraf(path.join(__dirname, 'libraries'));
});

it('should support named imports', async function () {
await fsFixture(overlayFS, dir)`
yarn.lock:
.parcelrc:
{
"extends": "@parcel/config-default",
"bundler": "@parcel/bundler-library"
}
package.json:
{
"module": "dist/out.js"
}
index.js:
export * from './foo';
export * from './bar';
foo.js:
import {baz} from './baz';
export function foo() {
return 'foo' + baz();
}
bar.js:
import {baz} from './baz';
export function bar() {
return 'bar' + baz();
}
baz.js:
export function baz() {
return 'baz';
}
`;

let b = await bundle(path.join(dir, '/index.js'), {
inputFS: overlayFS,
mode: 'production',
});

let res = await run(b);
assert.equal(res.foo(), 'foobaz');
assert.equal(res.bar(), 'barbaz');

assertBundles(b, [
{
assets: ['index.js'],
},
{
assets: ['foo.js'],
},
{
assets: ['bar.js'],
},
{
assets: ['baz.js'],
},
]);

for (let bundle of b.getBundles()) {
let contents = await outputFS.readFile(bundle.filePath, 'utf8');
assert(!contents.includes('parcelRequire'));
assert(contents.includes('export {'));
}
});
});
14 changes: 11 additions & 3 deletions packages/packagers/js/src/ScopeHoistingPackager.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,19 @@ export class ScopeHoistingPackager {
for (let b of this.bundleGraph.getReferencedBundles(this.bundle)) {
let entry = b.getMainEntry();
let symbols = new Map();
if (entry && !this.isAsyncBundle && entry.type === 'js') {
if (entry && entry.type === 'js') {
this.externalAssets.add(entry);

let usedSymbols = this.bundleGraph.getUsedSymbols(entry) || new Set();
let exportedSymbols = this.bundleGraph.getExportedSymbols(entry);
let hasNamespaceExport = exportedSymbols.some(
s => s.exportAs === '*',
);
if (usedSymbols.has('*') && !hasNamespaceExport) {
// Use all exported symbols if a namespace is requested but does not exist.
usedSymbols = new Set(exportedSymbols.map(e => e.exportSymbol));
}

for (let s of usedSymbols) {
// If the referenced bundle is ESM, and we are importing '*', use 'default' instead.
// This matches the logic below in buildExportedSymbols.
Expand Down Expand Up @@ -324,7 +333,6 @@ export class ScopeHoistingPackager {

if (
asset.meta.shouldWrap ||
this.isAsyncBundle ||
this.bundle.env.sourceType === 'script' ||
this.bundleGraph.isAssetReferenced(this.bundle, asset) ||
this.bundleGraph
Expand Down Expand Up @@ -361,7 +369,6 @@ export class ScopeHoistingPackager {

buildExportedSymbols() {
if (
this.isAsyncBundle ||
!this.bundle.env.isLibrary ||
this.bundle.env.outputFormat !== 'esmodule'
) {
Expand Down Expand Up @@ -1043,6 +1050,7 @@ ${code}
.some(
dep =>
!dep.isEntry &&
this.bundle.hasDependency(dep) &&
nullthrows(this.bundleGraph.getUsedSymbols(dep)).has('*'),
))) ||
// If a symbol is imported (used) from a CJS asset but isn't listed in the symbols,
Expand Down
2 changes: 1 addition & 1 deletion packages/runtimes/js/src/JSRuntime.js
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ export default (new Runtime({

// Skip URL runtimes for library builds. This is handled in packaging so that
// the url is inlined and statically analyzable.
if (bundle.env.isLibrary && dependency.meta?.placeholder != null) {
if (bundle.env.isLibrary && mainBundle.bundleBehavior !== 'isolated') {
continue;
}

Expand Down

0 comments on commit 597c63b

Please sign in to comment.