iifeify 0.1.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -3
- package/bin/cli.js +35 -2
- package/package.json +1 -1
- package/src/convert.js +11 -2
- package/src/install.js +8 -4
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Turn an npm package into one classic browser script.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
npx iifeify vanilla-jsoneditor@3.13.0 --entry standalone.js --global-name VanillaJsonEditor
|
|
6
|
+
npx -y iifeify vanilla-jsoneditor@3.13.0 --entry standalone.js --global-name VanillaJsonEditor
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
This writes `vanilla-jsoneditor.iife.js`. Loaded with a plain `<script>` tag, the file defines the global `VanillaJsonEditor`, which holds everything the package exports. The file starts with a license banner generated from the package's own metadata:
|
|
@@ -32,7 +32,7 @@ iifeify is for hosts that accept only classic scripts and cannot `import` anythi
|
|
|
32
32
|
Run it directly with `npx`, no installation required:
|
|
33
33
|
|
|
34
34
|
```bash
|
|
35
|
-
npx iifeify <package>
|
|
35
|
+
npx -y iifeify <package>
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
Or install it globally:
|
|
@@ -62,7 +62,18 @@ npx iifeify <package>[@version] [options]
|
|
|
62
62
|
| `-h`, `--help` | Show the help | |
|
|
63
63
|
| `-v`, `--version` | Show the version | |
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
While it runs, iifeify prints each step and how long it took on stderr, and the result on stdout:
|
|
66
|
+
|
|
67
|
+
```text
|
|
68
|
+
Installing vanilla-jsoneditor@3.13.0 with npm... done in 3.4s
|
|
69
|
+
Bundling vanilla-jsoneditor 3.13.0 with esbuild... done in 555ms
|
|
70
|
+
Wrote vanilla-jsoneditor.iife.js (1.21 MB)
|
|
71
|
+
package vanilla-jsoneditor 3.13.0 - ISC
|
|
72
|
+
entry standalone.js
|
|
73
|
+
global VanillaJsonEditor
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
A step that fails ends with `failed`, followed by the reason. The exit code is 0 on success, 1 if the package cannot be converted and 2 for a wrong command line.
|
|
66
77
|
|
|
67
78
|
### How it works
|
|
68
79
|
|
package/bin/cli.js
CHANGED
|
@@ -24,7 +24,7 @@ cli.command('<package>', 'Turn an npm package into one classic browser script th
|
|
|
24
24
|
.example(' $ iifeify ms')
|
|
25
25
|
.example(' $ iifeify vanilla-jsoneditor@3.13.0 --entry standalone.js --global-name VanillaJsonEditor')
|
|
26
26
|
.action(async (spec, options) => {
|
|
27
|
-
|
|
27
|
+
const result = await convert({
|
|
28
28
|
spec: String(spec),
|
|
29
29
|
entry: text(options.entry),
|
|
30
30
|
globalName: text(options.globalName),
|
|
@@ -32,11 +32,43 @@ cli.command('<package>', 'Turn an npm package into one classic browser script th
|
|
|
32
32
|
minify: options.minify,
|
|
33
33
|
target: text(options.target)?.split(','),
|
|
34
34
|
notices: options.notices,
|
|
35
|
-
|
|
35
|
+
onStep: step => startStep(step.name === 'install'
|
|
36
|
+
? `Installing ${step.spec} with npm`
|
|
37
|
+
: `Bundling ${step.label} with esbuild`),
|
|
38
|
+
});
|
|
39
|
+
endStep('done');
|
|
40
|
+
report(result);
|
|
36
41
|
});
|
|
37
42
|
cli.help();
|
|
38
43
|
cli.version(version);
|
|
39
44
|
|
|
45
|
+
// While a conversion runs, each step gets a line on stderr, like the warnings: what is being done,
|
|
46
|
+
// then how long it took, or that it failed. stdout gets the result only.
|
|
47
|
+
/** @type {number | undefined} */
|
|
48
|
+
let stepStartedAt;
|
|
49
|
+
|
|
50
|
+
/** @param {string} description */
|
|
51
|
+
function startStep(description) {
|
|
52
|
+
endStep('done');
|
|
53
|
+
process.stderr.write(`${description}... `);
|
|
54
|
+
stepStartedAt = performance.now();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** @param {'done' | 'failed'} outcome */
|
|
58
|
+
function endStep(outcome) {
|
|
59
|
+
if (stepStartedAt === undefined) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const ms = performance.now() - stepStartedAt;
|
|
63
|
+
stepStartedAt = undefined;
|
|
64
|
+
if (outcome === 'failed') {
|
|
65
|
+
process.stderr.write('failed\n\n'); // the error follows after a blank line
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const took = ms < 1000 ? `${Math.round(ms)}ms` : `${(ms / 1000).toFixed(1)}s`;
|
|
69
|
+
process.stderr.write(`done in ${took}\n`);
|
|
70
|
+
}
|
|
71
|
+
|
|
40
72
|
/** @param {Awaited<ReturnType<typeof convert>>} result */
|
|
41
73
|
function report({ files, bytes, packages, entry, globalName, warnings, hint }) {
|
|
42
74
|
const [root, ...dependencies] = packages;
|
|
@@ -79,6 +111,7 @@ try {
|
|
|
79
111
|
cli.parse(process.argv, { run: false });
|
|
80
112
|
await cli.runMatchedCommand();
|
|
81
113
|
} catch (error) {
|
|
114
|
+
endStep('failed');
|
|
82
115
|
// cac does not export the class of the errors it throws for a wrong command line.
|
|
83
116
|
if (error instanceof UsageError || error.name === 'CACError') {
|
|
84
117
|
console.error(`${error.message}\nRun "iifeify --help" for the options.`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iifeify",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Turn any browser-compatible npm package into a single classic script (an IIFE that defines one global), with a generated license banner.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Florian Noever",
|
package/src/convert.js
CHANGED
|
@@ -7,6 +7,12 @@ import { bundle, checkOptions } from './bundle.js';
|
|
|
7
7
|
import { installPackage, parseSpec } from './install.js';
|
|
8
8
|
import { bundledPackages } from './packages.js';
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* @typedef {{ name: 'install', spec: string } | { name: 'bundle', label: string }} Step
|
|
12
|
+
* a step of a conversion: npm installs the package as the user named it, or esbuild bundles
|
|
13
|
+
* the installed package, labelled with its name and version such as "ms 2.1.3"
|
|
14
|
+
*/
|
|
15
|
+
|
|
10
16
|
/**
|
|
11
17
|
* Installs an npm package into a temporary folder and writes it as one classic browser script.
|
|
12
18
|
*
|
|
@@ -18,10 +24,11 @@ import { bundledPackages } from './packages.js';
|
|
|
18
24
|
* @param {boolean} [options.minify]
|
|
19
25
|
* @param {string[]} [options.target] esbuild targets such as ["es2020"]
|
|
20
26
|
* @param {boolean} [options.notices] also write the full license texts next to the script
|
|
27
|
+
* @param {(step: Step) => void} [options.onStep] called as each step starts, to report progress
|
|
21
28
|
* @returns {Promise<{ files: { js: string, css?: string, notices?: string }, bytes: number,
|
|
22
29
|
* packages: object[], entry: string, globalName: string, warnings: string[], hint?: string }>}
|
|
23
30
|
*/
|
|
24
|
-
export async function convert({ spec, entry, globalName: requestedGlobalName, outfile, minify = true, target, notices = false }) {
|
|
31
|
+
export async function convert({ spec, entry, globalName: requestedGlobalName, outfile, minify = true, target, notices = false, onStep }) {
|
|
25
32
|
const wanted = parseSpec(spec);
|
|
26
33
|
await checkOptions({ globalName: requestedGlobalName, target });
|
|
27
34
|
|
|
@@ -29,10 +36,12 @@ export async function convert({ spec, entry, globalName: requestedGlobalName, ou
|
|
|
29
36
|
// reports files by their real paths.
|
|
30
37
|
const workDir = realpathSync.native(mkdtempSync(join(tmpdir(), 'iifeify-')));
|
|
31
38
|
try {
|
|
32
|
-
|
|
39
|
+
onStep?.({ name: 'install', spec });
|
|
40
|
+
const { dir, manifest } = await installPackage(wanted, workDir);
|
|
33
41
|
const globalName = requestedGlobalName ?? defaultGlobalName(manifest.name);
|
|
34
42
|
|
|
35
43
|
const label = `${manifest.name} ${manifest.version}`;
|
|
44
|
+
onStep?.({ name: 'bundle', label });
|
|
36
45
|
const built = await bundle({ workDir, name: wanted.name, dir, label, entry, globalName, minify, target });
|
|
37
46
|
const packages = bundledPackages(built.inputs, dir);
|
|
38
47
|
const banner = renderBanner(packages);
|
package/src/install.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { exec } from 'node:child_process';
|
|
2
2
|
import { readFileSync, realpathSync, writeFileSync } from 'node:fs';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
|
+
import { promisify } from 'node:util';
|
|
4
5
|
|
|
5
6
|
import { ConversionError, UsageError } from './errors.js';
|
|
6
7
|
|
|
8
|
+
// Asynchronous, so that the caller can report progress while npm works.
|
|
9
|
+
const run = promisify(exec);
|
|
10
|
+
|
|
7
11
|
// npm's rule for package names, including old names with capital letters.
|
|
8
12
|
const PACKAGE_NAME = /^(@[a-z0-9~-][a-z0-9._~-]*\/)?[a-z0-9~-][a-z0-9._~-]*$/i;
|
|
9
13
|
|
|
@@ -29,9 +33,9 @@ export function parseSpec(spec) {
|
|
|
29
33
|
/**
|
|
30
34
|
* Installs the package and its dependencies into workDir/node_modules.
|
|
31
35
|
*
|
|
32
|
-
* @returns {{ dir: string, manifest: object }} the package's folder and its package.json
|
|
36
|
+
* @returns {Promise<{ dir: string, manifest: object }>} the package's folder and its package.json
|
|
33
37
|
*/
|
|
34
|
-
export function installPackage({ name, range }, workDir) {
|
|
38
|
+
export async function installPackage({ name, range }, workDir) {
|
|
35
39
|
// A package.json of our own makes workDir the project root. Without one, npm installs into the
|
|
36
40
|
// nearest parent folder that has a package.json, and the temp folder lies inside the user
|
|
37
41
|
// profile. It also keeps the range off the command line: on Windows npm starts through cmd.exe,
|
|
@@ -39,7 +43,7 @@ export function installPackage({ name, range }, workDir) {
|
|
|
39
43
|
writeFileSync(join(workDir, 'package.json'), JSON.stringify({ private: true, dependencies: { [name]: range } }));
|
|
40
44
|
|
|
41
45
|
try {
|
|
42
|
-
|
|
46
|
+
await run(INSTALL, { cwd: workDir, encoding: 'utf8', windowsHide: true, maxBuffer: 64 * 1024 * 1024 });
|
|
43
47
|
} catch (error) {
|
|
44
48
|
const output = String(error.stderr ?? '').trim() || error.message;
|
|
45
49
|
throw new ConversionError(`Could not install ${name}@${range}.\n\n${output}`);
|