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/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 * as acorn from 'acorn';
5
- import * as walk from 'acorn-walk';
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
- try {
124
- for (const spec of jsImports(s.inline.code, true)) if (/^\.{0,2}\//.test(spec)) check(rel, spec, baseDir);
125
- } catch (err) {
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
- try {
138
- for (const spec of jsImports(o.data.toString('utf8'), !!o.module)) if (/^\.{0,2}\//.test(spec)) check(rel, spec, dir);
139
- } catch (err) {
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
+ });