vanilla-builder 0.1.0 → 0.2.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/CHANGELOG.md +20 -0
- package/README.es.md +76 -7
- package/README.md +76 -7
- package/bin/builder.js +106 -2
- package/package.json +7 -3
- package/src/builder.js +272 -89
- package/src/compress.js +44 -0
- package/src/config.js +197 -190
- package/src/deps.js +60 -1
- package/src/files.js +7 -0
- package/src/js.js +26 -34
- package/src/jsimports.js +54 -0
- package/src/jsinfo.js +19 -3
- package/src/pool.js +341 -0
- package/src/report.js +14 -19
- package/src/tersercore.js +33 -0
- package/src/urls.js +3 -0
- package/src/util.js +5 -1
- package/src/verify.js +13 -23
- package/src/worker.js +26 -0
package/src/verify.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// Verificación de la salida antes de publicarla: referencias, sintaxis, escritura y cobertura.
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
-
import
|
|
5
|
-
import
|
|
4
|
+
import { jsImports } from './jsimports.js';
|
|
5
|
+
import { mapLimit } from './compress.js';
|
|
6
6
|
import { parseHtml } from './html.js';
|
|
7
7
|
import { classifyUrl, parseSrcset } from './urls.js';
|
|
8
8
|
|
|
@@ -54,17 +54,6 @@ export function cssRefs(css) {
|
|
|
54
54
|
return refs;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
function jsImports(code, isModule) {
|
|
58
|
-
const ast = acorn.parse(code, { ecmaVersion: 'latest', sourceType: isModule ? 'module' : 'script', allowHashBang: true });
|
|
59
|
-
const out = [];
|
|
60
|
-
walk.full(ast, (n) => {
|
|
61
|
-
if ((n.type === 'ImportDeclaration' || n.type === 'ExportAllDeclaration' || n.type === 'ExportNamedDeclaration') && n.source)
|
|
62
|
-
out.push(n.source.value);
|
|
63
|
-
if (n.type === 'ImportExpression' && n.source.type === 'Literal' && typeof n.source.value === 'string') out.push(n.source.value);
|
|
64
|
-
});
|
|
65
|
-
return out;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
57
|
/**
|
|
69
58
|
* @param {string} staging carpeta temporal escrita
|
|
70
59
|
* @param {Map<string, object>} outputs archivos generados (rel → {data|copyFrom, type, size})
|
|
@@ -97,6 +86,11 @@ export async function verifyOutput(staging, outputs, ctx) {
|
|
|
97
86
|
}
|
|
98
87
|
};
|
|
99
88
|
|
|
89
|
+
// Parseo de los JS generados: en paralelo (hilos) y con caché por contenido; el resultado es el mismo.
|
|
90
|
+
const parse = (code, isModule) => (ctx.heavy ? ctx.heavy.imports(code, isModule) : jsImports(code, isModule));
|
|
91
|
+
const jsList = [...outputs].filter(([, o]) => o.type === 'js' && !o.copyFrom);
|
|
92
|
+
const parsed = new Map(await mapLimit(jsList, 64, async ([rel, o]) => [rel, await parse(o.data.toString('utf8'), !!o.module)]));
|
|
93
|
+
|
|
100
94
|
for (const [rel, o] of outputs) {
|
|
101
95
|
stats.files++;
|
|
102
96
|
const dest = path.join(staging, rel);
|
|
@@ -120,11 +114,9 @@ export async function verifyOutput(staging, outputs, ctx) {
|
|
|
120
114
|
for (const s of model.scripts) {
|
|
121
115
|
if (s.src) check(rel, s.src.value, baseDir);
|
|
122
116
|
if (s.inline && s.kind === 'module') {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
errors.push(`${rel}: un <script type="module"> inline no es JS válido (${err.message}).`);
|
|
127
|
-
}
|
|
117
|
+
const r = jsImports(s.inline.code, true);
|
|
118
|
+
if (r.error) errors.push(`${rel}: un <script type="module"> inline no es JS válido (${r.error}).`);
|
|
119
|
+
else for (const spec of r.imports) if (/^\.{0,2}\//.test(spec)) check(rel, spec, baseDir);
|
|
128
120
|
}
|
|
129
121
|
}
|
|
130
122
|
for (const l of model.links) if (l.type === 'stylesheet' || l.type === 'asset' || l.type === 'preload') check(rel, l.href.value, baseDir);
|
|
@@ -134,11 +126,9 @@ export async function verifyOutput(staging, outputs, ctx) {
|
|
|
134
126
|
for (const u of cssRefs(o.data.toString('utf8'))) check(rel, u, dir);
|
|
135
127
|
} else if (o.type === 'js') {
|
|
136
128
|
stats.js++;
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
errors.push(`${rel}: el JavaScript generado no es válido (${err.message}).`);
|
|
141
|
-
}
|
|
129
|
+
const r = parsed.get(rel);
|
|
130
|
+
if (r.error) errors.push(`${rel}: el JavaScript generado no es válido (${r.error}).`);
|
|
131
|
+
else for (const spec of r.imports) if (/^\.{0,2}\//.test(spec)) check(rel, spec, dir);
|
|
142
132
|
}
|
|
143
133
|
}
|
|
144
134
|
|
package/src/worker.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Hilo de trabajo: ofusca o comprime con terser fuera del hilo principal.
|
|
2
|
+
import { parentPort, workerData } from 'node:worker_threads';
|
|
3
|
+
import { ensureObfuscator, obfuscateSafe } from './obfuscate.js';
|
|
4
|
+
import { importOptional } from './optional.js';
|
|
5
|
+
import { terserMinify } from './tersercore.js';
|
|
6
|
+
import { jsImports } from './jsimports.js';
|
|
7
|
+
|
|
8
|
+
let terser = null;
|
|
9
|
+
|
|
10
|
+
parentPort.on('message', async ({ id, kind, payload }) => {
|
|
11
|
+
try {
|
|
12
|
+
let result;
|
|
13
|
+
if (kind === 'obfuscate') {
|
|
14
|
+
await ensureObfuscator(workerData.root);
|
|
15
|
+
result = obfuscateSafe(payload.code, payload.opts);
|
|
16
|
+
} else if (kind === 'terser') {
|
|
17
|
+
terser ??= await importOptional('terser', workerData.root, 'para la compresión máxima');
|
|
18
|
+
result = await terserMinify(terser, payload.code, payload.opts);
|
|
19
|
+
} else if (kind === 'imports') {
|
|
20
|
+
result = jsImports(payload.code, payload.opts.isModule);
|
|
21
|
+
} else throw new Error(`tarea desconocida: ${kind}`);
|
|
22
|
+
parentPort.postMessage({ id, result });
|
|
23
|
+
} catch (err) {
|
|
24
|
+
parentPort.postMessage({ id, error: String(err?.message ?? err) });
|
|
25
|
+
}
|
|
26
|
+
});
|