Skip to content
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

feat(bundle-source): Add tag command-line flag #2353

Merged
merged 1 commit into from
Jul 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/bundle-source/NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
- Adds a `-f,--format` command flag to specify other module formats.
- Adds a new `endoScript` module format.
- Adds a no-cache, bundle-to-stdout mode.
- Adds a `-t,--tag` command flag to specify export/import conditions like
`"browser"`.

# v3.2.1 (2024-03-20)

Expand Down
35 changes: 32 additions & 3 deletions packages/bundle-source/cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const { Fail, quote: q } = assert;
* @property {number} bundleSize
* @property {boolean} noTransforms
* @property {ModuleFormat} format
* @property {string[]} tags
* @property {{ relative: string, absolute: string }} moduleSource
* @property {Array<{ relativePath: string, mtime: string, size: number }>} contents
*/
Expand Down Expand Up @@ -51,12 +52,19 @@ export const makeBundleCache = (wr, cwd, readPowers, opts) => {
* @param {Logger} [log]
* @param {object} [options]
* @param {boolean} [options.noTransforms]
* @param {string[]} [options.tags]
* @param {ModuleFormat} [options.format]
*/
const add = async (rootPath, targetName, log = defaultLog, options = {}) => {
const srcRd = cwd.neighbor(rootPath);

const { noTransforms = false, format = 'endoZipBase64' } = options;
const {
noTransforms = false,
format = 'endoZipBase64',
tags = [],
} = options;

tags.sort();

const statsByPath = new Map();

Expand Down Expand Up @@ -115,6 +123,7 @@ export const makeBundleCache = (wr, cwd, readPowers, opts) => {
),
noTransforms,
format,
tags,
};

await metaWr.atomicWriteText(JSON.stringify(meta, null, 2));
Expand Down Expand Up @@ -144,6 +153,7 @@ export const makeBundleCache = (wr, cwd, readPowers, opts) => {
* @param {object} [options]
* @param {boolean} [options.noTransforms]
* @param {ModuleFormat} [options.format]
* @param {string[]} [options.tags]
* @returns {Promise<BundleMeta>}
*/
const validate = async (
Expand All @@ -154,8 +164,12 @@ export const makeBundleCache = (wr, cwd, readPowers, opts) => {
options = {},
) => {
await null;
const { noTransforms: expectedNoTransforms, format: expectedFormat } =
options;
const {
noTransforms: expectedNoTransforms,
format: expectedFormat,
tags: expectedTags = [],
} = options;
expectedTags.sort();
if (!meta) {
const metaJson = await loadMetaText(targetName, log);
if (metaJson) {
Expand All @@ -180,10 +194,16 @@ export const makeBundleCache = (wr, cwd, readPowers, opts) => {
moduleSource: { absolute: moduleSource },
format = 'endoZipBase64',
noTransforms = false,
tags = [],
} = meta;
tags.sort();
assert.equal(bundleFileName, toBundleName(targetName));
assert.equal(format, expectedFormat);
assert.equal(noTransforms, expectedNoTransforms);
assert.equal(tags.length, expectedTags.length);
tags.forEach((tag, index) => {
assert.equal(tag, expectedTags[index]);
});
if (rootOpt) {
moduleSource === cwd.neighbor(rootOpt).absolute() ||
Fail`bundle ${targetName} was for ${moduleSource}, not ${rootOpt}`;
Expand Down Expand Up @@ -230,6 +250,7 @@ export const makeBundleCache = (wr, cwd, readPowers, opts) => {
* @param {object} [options]
* @param {boolean} [options.noTransforms]
* @param {ModuleFormat} [options.format]
* @param {string[]} [options.tags]
* @returns {Promise<BundleMeta>}
*/
const validateOrAdd = async (
Expand All @@ -248,13 +269,15 @@ export const makeBundleCache = (wr, cwd, readPowers, opts) => {
meta = await validate(targetName, rootPath, log, meta, {
format: options.format,
noTransforms: options.noTransforms,
tags: options.tags,
});
const {
bundleTime,
bundleSize,
contents,
noTransforms,
format = 'endoZipBase64',
tags = [],
} = meta;
log(
`${wr}`,
Expand All @@ -268,6 +291,8 @@ export const makeBundleCache = (wr, cwd, readPowers, opts) => {
noTransforms ? 'w/o transforms' : 'with transforms',
'with format',
format,
'and tags',
JSON.stringify(tags),
);
} catch (invalid) {
meta = undefined;
Expand All @@ -284,6 +309,7 @@ export const makeBundleCache = (wr, cwd, readPowers, opts) => {
contents,
noTransforms,
format = 'endoZipBase64',
tags = [],
} = meta;
log(
`${wr}`,
Expand All @@ -296,6 +322,8 @@ export const makeBundleCache = (wr, cwd, readPowers, opts) => {
noTransforms ? 'w/o transforms' : 'with transforms',
'with format',
format,
'and tags',
JSON.stringify(tags),
);
}

Expand All @@ -310,6 +338,7 @@ export const makeBundleCache = (wr, cwd, readPowers, opts) => {
* @param {object} [options]
* @param {boolean} [options.noTransforms]
* @param {ModuleFormat} [options.format]
* @param {string[]} [options.tags]
*/
const load = async (
rootPath,
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 15 additions & 3 deletions packages/bundle-source/src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import { jsOpts, jsonOpts, makeNodeBundleCache } from '../cache.js';
/** @import {ModuleFormat} from './types.js' */

const USAGE = `\
bundle-source [-Tf] <entry.js>
bundle-source [-Tf] --cache-js|--cache-json <cache/> (<entry.js> <bundle-name>)*
bundle-source [-Tft] <entry.js>
bundle-source [-Tft] --cache-js|--cache-json <cache/> (<entry.js> <bundle-name>)*
-f,--format endoZipBase64*|nestedEvaluate|getExport
-t,--tag <tag> (browser, node, &c)
-T,--no-transforms`;

const options = /** @type {const} */ ({
Expand All @@ -32,6 +33,11 @@ const options = /** @type {const} */ ({
short: 'f',
multiple: false,
},
tag: {
type: 'string',
short: 't',
multiple: true,
},
// deprecated
to: {
type: 'string',
Expand All @@ -52,6 +58,7 @@ export const main = async (args, { loadModule, pid, log }) => {
const {
values: {
format: moduleFormat = 'endoZipBase64',
tag: tags = [],
'no-transforms': noTransforms,
'cache-json': cacheJson,
'cache-js': cacheJs,
Expand Down Expand Up @@ -84,7 +91,11 @@ export const main = async (args, { loadModule, pid, log }) => {
throw new Error(USAGE);
}
const [entryPath] = positionals;
const bundle = await bundleSource(entryPath, { noTransforms, format });
const bundle = await bundleSource(entryPath, {
noTransforms,
format,
tags,
});
process.stdout.write(JSON.stringify(bundle));
process.stdout.write('\n');
return;
Expand Down Expand Up @@ -114,6 +125,7 @@ export const main = async (args, { loadModule, pid, log }) => {
await cache.validateOrAdd(bundleRoot, bundleName, undefined, {
noTransforms,
format,
tags,
});
}
};
2 changes: 2 additions & 0 deletions packages/bundle-source/src/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export async function bundleScript(
dev = false,
cacheSourceMaps = false,
noTransforms = false,
tags = [],
commonDependencies,
} = options;
const powers = { ...readPowers, ...grantedPowers };
Expand Down Expand Up @@ -69,6 +70,7 @@ export async function bundleScript(

const source = await makeBundle(powers, entry, {
dev,
tags,
commonDependencies,
parserForLanguage,
moduleTransforms,
Expand Down
2 changes: 2 additions & 0 deletions packages/bundle-source/src/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ export {};
* @property {boolean} [noTransforms] - when true, generates a bundle with the
* original sources instead of SES-shim specific ESM and CJS. This may become
* default in a future major version.
* @property {string[]} [tags] - conditions for package.json conditional
* exports and imports.
*/

/**
Expand Down
3 changes: 3 additions & 0 deletions packages/bundle-source/src/zip-base64.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const readPowers = makeReadPowers({ fs, url, crypto });
* @param {boolean} [options.dev]
* @param {boolean} [options.cacheSourceMaps]
* @param {boolean} [options.noTransforms]
* @param {string[]} [options.tags]
* @param {Record<string, string>} [options.commonDependencies]
* @param {object} [grantedPowers]
* @param {(bytes: string | Uint8Array) => string} [grantedPowers.computeSha512]
Expand All @@ -39,6 +40,7 @@ export async function bundleZipBase64(
dev = false,
cacheSourceMaps = false,
noTransforms = false,
tags = [],
commonDependencies,
} = options;
const powers = { ...readPowers, ...grantedPowers };
Expand Down Expand Up @@ -71,6 +73,7 @@ export async function bundleZipBase64(

const compartmentMap = await mapNodeModules(powers, entry, {
dev,
tags,
commonDependencies,
});

Expand Down
67 changes: 67 additions & 0 deletions packages/bundle-source/test/tags-command.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/* global Buffer */
import test from '@endo/ses-ava/prepare-endo.js';

import { spawn } from 'child_process';
import url from 'url';

const textDecoder = new TextDecoder();

const cwd = url.fileURLToPath(new URL('..', import.meta.url));

const bundleSource = async (...args) => {
const bundleBytes = await new Promise((resolve, reject) => {
const errorChunks = [];
const outputChunks = [];
const child = spawn('node', ['bin/bundle-source', ...args], {
cwd,
stdio: ['inherit', 'pipe', 'pipe'],
});
child.on('close', code => {
if (code !== 0) {
reject(
new Error(
`Exit code: ${code}\nError output: ${new TextDecoder().decode(
Buffer.concat(errorChunks),
)}`,
),
);
} else {
resolve(Buffer.concat(outputChunks));
}
});
child.stdout.on('data', chunk => {
outputChunks.push(chunk);
});
child.stderr.on('data', chunk => {
errorChunks.push(chunk);
});
});
const bundleText = textDecoder.decode(bundleBytes);
return JSON.parse(bundleText);
};

test('bundle-source with --format and --tag', async t => {
const compartment = new Compartment();
{
const bundle = await bundleSource(
'--tag',
'b',
'--format',
'endoScript',
'demo/node_modules/conditional-reexports/entry.js',
);
const namespace = compartment.evaluate(bundle.source);
t.is(namespace.default, 'b');
}
{
const bundle = await bundleSource(
'--tag',
'a',
'--format',
'endoScript',
'demo/node_modules/conditional-reexports/entry.js',
);
const namespace = compartment.evaluate(bundle.source);
t.is(namespace.default, 'a');
}
});
Loading