@caryhu/codemine-forge 0.0.1-alpha.1 → 0.0.1-alpha.2
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/dist/build-pipeline/browser-bundle.d.ts +2 -1
- package/dist/build-pipeline/browser-bundle.js +384 -24
- package/dist/build-pipeline/diagnostics.js +1 -1
- package/dist/build-pipeline/index.d.ts +1 -1
- package/dist/build-pipeline/pipeline.js +28 -30
- package/dist/build-pipeline/types.d.ts +18 -0
- package/dist/build-pipeline/vite-config.d.ts +8 -0
- package/dist/build-pipeline/vite-config.js +215 -0
- package/dist/dev-server-preview/index.d.ts +2 -2
- package/dist/dev-server-preview/index.js +2 -1
- package/dist/dev-server-preview/preview-document.d.ts +2 -1
- package/dist/dev-server-preview/preview-document.js +203 -1
- package/dist/dev-server-preview/server.d.ts +8 -1
- package/dist/dev-server-preview/server.js +246 -32
- package/dist/dev-server-preview/types.d.ts +38 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -2
- package/package.json +2 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ForgePackageResolutionResult } from "../package-resolution";
|
|
2
2
|
import { type ForgeVirtualFileSystem } from "../virtual-file-system";
|
|
3
|
-
import type { ForgeBrowserBundle, ForgeBuildOutputFile, ForgeBuildPipelineDiagnostic } from "./types";
|
|
3
|
+
import type { ForgeBrowserBundle, ForgeBuildOutputFile, ForgeBuildPipelineDiagnostic, ForgeViteConfigMetadata } from "./types";
|
|
4
4
|
export interface ForgeBrowserBundleResult {
|
|
5
5
|
readonly browserBundle?: ForgeBrowserBundle;
|
|
6
6
|
readonly diagnostics: readonly ForgeBuildPipelineDiagnostic[];
|
|
@@ -11,6 +11,7 @@ interface CreateBrowserBundleOptions {
|
|
|
11
11
|
readonly fileSystem: ForgeVirtualFileSystem;
|
|
12
12
|
readonly packageResolution: ForgePackageResolutionResult;
|
|
13
13
|
readonly scriptTransformer?: ForgeBrowserBundleScriptTransformer;
|
|
14
|
+
readonly viteConfig?: ForgeViteConfigMetadata;
|
|
14
15
|
}
|
|
15
16
|
export type ForgeBrowserBundleScriptLoader = "ts" | "tsx";
|
|
16
17
|
export interface ForgeBrowserBundleScriptTransformRequest {
|
|
@@ -36,6 +36,7 @@ const DEFAULT_RESOLVE_EXTENSIONS = [
|
|
|
36
36
|
".js",
|
|
37
37
|
".json",
|
|
38
38
|
".css",
|
|
39
|
+
".vue",
|
|
39
40
|
];
|
|
40
41
|
const INDEX_RESOLVE_EXTENSIONS = [
|
|
41
42
|
"/index.tsx",
|
|
@@ -44,6 +45,7 @@ const INDEX_RESOLVE_EXTENSIONS = [
|
|
|
44
45
|
"/index.js",
|
|
45
46
|
"/index.json",
|
|
46
47
|
"/index.css",
|
|
48
|
+
"/index.vue",
|
|
47
49
|
];
|
|
48
50
|
const SCRIPT_EXTENSIONS = new Set([".js", ".jsx", ".ts", ".tsx"]);
|
|
49
51
|
async function createBrowserBundle(options) {
|
|
@@ -64,7 +66,8 @@ async function createBrowserBundle(options) {
|
|
|
64
66
|
const extension = getExtension(sourcePath);
|
|
65
67
|
if (!SCRIPT_EXTENSIONS.has(extension) &&
|
|
66
68
|
extension !== ".css" &&
|
|
67
|
-
extension !== ".json"
|
|
69
|
+
extension !== ".json" &&
|
|
70
|
+
extension !== ".vue") {
|
|
68
71
|
modules.set(sourcePath, {
|
|
69
72
|
outputPath: createModuleOutputPath(sourcePath),
|
|
70
73
|
sourcePath,
|
|
@@ -74,12 +77,24 @@ async function createBrowserBundle(options) {
|
|
|
74
77
|
return;
|
|
75
78
|
}
|
|
76
79
|
const source = await options.fileSystem.readText(sourcePath);
|
|
77
|
-
const
|
|
80
|
+
const importSource = extension === ".vue" ? createVueSfcModule(sourcePath, source) : source;
|
|
81
|
+
const imports = extractImportReferences(importSource).map((reference) => reference.specifier);
|
|
78
82
|
const rewrites = new Map();
|
|
79
83
|
for (const specifier of imports) {
|
|
80
84
|
if (isExternalUrl(specifier)) {
|
|
81
85
|
continue;
|
|
82
86
|
}
|
|
87
|
+
const aliasSpecifier = resolveAliasSpecifier(specifier, options.viteConfig?.aliases ?? {});
|
|
88
|
+
if (aliasSpecifier !== undefined) {
|
|
89
|
+
const resolvedPath = await resolveLocalImport(options.fileSystem, sourcePath, aliasSpecifier);
|
|
90
|
+
if (resolvedPath === undefined) {
|
|
91
|
+
diagnostics.push((0, diagnostics_1.createUnresolvedImportDiagnostic)(specifier, sourcePath));
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
rewrites.set(specifier, createModuleOutputPath(resolvedPath));
|
|
95
|
+
await visitWorkspaceModule(resolvedPath);
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
83
98
|
if (isLocalSpecifier(specifier)) {
|
|
84
99
|
const resolvedPath = await resolveLocalImport(options.fileSystem, sourcePath, specifier);
|
|
85
100
|
if (resolvedPath === undefined) {
|
|
@@ -102,7 +117,7 @@ async function createBrowserBundle(options) {
|
|
|
102
117
|
await visitPackageModule(packageImport.packageRecord, packageImport.filePath);
|
|
103
118
|
}
|
|
104
119
|
}
|
|
105
|
-
const transformResult = await transformWorkspaceModule(sourcePath, source, options.scriptTransformer);
|
|
120
|
+
const transformResult = await transformWorkspaceModule(sourcePath, source, options.scriptTransformer, options.viteConfig?.define ?? {});
|
|
106
121
|
if (!transformResult.success) {
|
|
107
122
|
diagnostics.push(...transformResult.diagnostics);
|
|
108
123
|
visiting.delete(sourcePath);
|
|
@@ -196,6 +211,7 @@ async function createBrowserBundle(options) {
|
|
|
196
211
|
const localImports = Object.fromEntries(moduleOutputFiles.map((file) => [file.path, file.path]));
|
|
197
212
|
const packageImportEntries = Object.fromEntries(Array.from(packageImports.entries()).sort(([left], [right]) => left.localeCompare(right)));
|
|
198
213
|
const packageEntryImports = createPackageEntryImports(options.packageResolution.packages);
|
|
214
|
+
const bundleModules = createBrowserBundleModules(Array.from(modules.values()), Array.from(packageModules.values()));
|
|
199
215
|
const importMap = {
|
|
200
216
|
imports: sortRecord({
|
|
201
217
|
...packageEntryImports,
|
|
@@ -213,34 +229,60 @@ async function createBrowserBundle(options) {
|
|
|
213
229
|
format: "esm",
|
|
214
230
|
importMap,
|
|
215
231
|
importMapPath: BROWSER_IMPORT_MAP_PATH,
|
|
216
|
-
moduleCount:
|
|
232
|
+
moduleCount: bundleModules.length,
|
|
233
|
+
modules: bundleModules,
|
|
217
234
|
outputPaths: allOutputFiles.map((file) => file.path),
|
|
235
|
+
viteConfig: options.viteConfig,
|
|
218
236
|
},
|
|
219
237
|
diagnostics: [],
|
|
220
238
|
outputFiles: allOutputFiles,
|
|
221
239
|
};
|
|
222
240
|
}
|
|
223
241
|
exports.createBrowserBundle = createBrowserBundle;
|
|
224
|
-
|
|
242
|
+
function createBrowserBundleModules(workspaceModules, packageModules) {
|
|
243
|
+
return [
|
|
244
|
+
...workspaceModules.map((moduleRecord) => ({
|
|
245
|
+
origin: "workspace",
|
|
246
|
+
outputPath: moduleRecord.outputPath,
|
|
247
|
+
sourcePath: moduleRecord.sourcePath,
|
|
248
|
+
})),
|
|
249
|
+
...packageModules.map((moduleRecord) => ({
|
|
250
|
+
origin: "package",
|
|
251
|
+
outputPath: moduleRecord.outputPath,
|
|
252
|
+
sourcePath: createPackageImporterPath(moduleRecord.packageRecord, moduleRecord.filePath),
|
|
253
|
+
})),
|
|
254
|
+
].sort((left, right) => left.outputPath.localeCompare(right.outputPath));
|
|
255
|
+
}
|
|
256
|
+
async function transformWorkspaceModule(sourcePath, source, scriptTransformer, defineReplacements) {
|
|
225
257
|
const extension = getExtension(sourcePath);
|
|
226
258
|
if (extension === ".css") {
|
|
227
|
-
return transformSuccess(
|
|
259
|
+
return transformSuccess(createWorkspaceCssModule(sourcePath, source));
|
|
228
260
|
}
|
|
229
261
|
if (extension === ".json") {
|
|
230
262
|
return transformSuccess(createJsonModule(source));
|
|
231
263
|
}
|
|
264
|
+
const applyDefines = (outputText) => applyDefineReplacements(outputText, defineReplacements);
|
|
265
|
+
if (extension === ".vue") {
|
|
266
|
+
return transformSuccess(applyDefines(transpileScript(sourcePath, createVueSfcModule(sourcePath, source))));
|
|
267
|
+
}
|
|
232
268
|
if (!SCRIPT_EXTENSIONS.has(extension)) {
|
|
233
269
|
return transformSuccess(source);
|
|
234
270
|
}
|
|
235
271
|
const loader = scriptLoaderForExtension(extension);
|
|
236
272
|
if (loader !== undefined && scriptTransformer !== undefined) {
|
|
237
|
-
|
|
273
|
+
const transformResult = await scriptTransformer({
|
|
238
274
|
loader,
|
|
239
275
|
sourcePath,
|
|
240
276
|
sourceText: source,
|
|
241
277
|
});
|
|
278
|
+
return transformResult.success
|
|
279
|
+
? transformSuccess(applyDefines(transformResult.outputText))
|
|
280
|
+
: transformResult;
|
|
242
281
|
}
|
|
243
|
-
return transformSuccess(
|
|
282
|
+
return transformSuccess(applyDefines(transpileScript(sourcePath, source)));
|
|
283
|
+
}
|
|
284
|
+
function transpileScript(sourcePath, source) {
|
|
285
|
+
return ts.transpileModule(source, {
|
|
244
286
|
compilerOptions: {
|
|
245
287
|
esModuleInterop: true,
|
|
246
288
|
importsNotUsedAsValues: ts.ImportsNotUsedAsValues.Remove,
|
|
@@ -252,7 +294,7 @@ async function transformWorkspaceModule(sourcePath, source, scriptTransformer) {
|
|
|
252
294
|
},
|
|
253
295
|
fileName: sourcePath,
|
|
254
296
|
reportDiagnostics: false,
|
|
255
|
-
}).outputText
|
|
297
|
+
}).outputText;
|
|
256
298
|
}
|
|
257
299
|
function transformSuccess(outputText) {
|
|
258
300
|
return {
|
|
@@ -271,7 +313,7 @@ function transformPackageModule(importer, filePath, content) {
|
|
|
271
313
|
}
|
|
272
314
|
const source = typeof content === "string" ? content : new TextDecoder().decode(content);
|
|
273
315
|
if (extension === ".css") {
|
|
274
|
-
return transformSuccess(
|
|
316
|
+
return transformSuccess(createPlainCssModule(importer, source));
|
|
275
317
|
}
|
|
276
318
|
if (extension === ".json") {
|
|
277
319
|
return transformSuccess(createJsonModule(source));
|
|
@@ -279,21 +321,244 @@ function transformPackageModule(importer, filePath, content) {
|
|
|
279
321
|
if (!SCRIPT_EXTENSIONS.has(extension)) {
|
|
280
322
|
return transformSuccess(source);
|
|
281
323
|
}
|
|
282
|
-
return transformSuccess(
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
},
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
324
|
+
return transformSuccess(transpileScript(importer, source));
|
|
325
|
+
}
|
|
326
|
+
function createVueSfcModule(sourcePath, source) {
|
|
327
|
+
const scriptSetup = extractVueSfcBlock(source, "script", "setup") ?? "";
|
|
328
|
+
const template = extractVueSfcBlock(source, "template") ?? "";
|
|
329
|
+
const bindings = collectVueSetupBindings(scriptSetup);
|
|
330
|
+
const renderExpression = createVueRenderExpression(template);
|
|
331
|
+
const setupReturn = bindings.length === 0
|
|
332
|
+
? "{}"
|
|
333
|
+
: `{ ${bindings.map((binding) => `${binding}: ${binding}`).join(", ")} }`;
|
|
334
|
+
return [
|
|
335
|
+
'import { h as __forgeVueH } from "vue";',
|
|
336
|
+
scriptSetup,
|
|
337
|
+
"",
|
|
338
|
+
"const __forgeVueComponent = {",
|
|
339
|
+
` __file: ${JSON.stringify(sourcePath)},`,
|
|
340
|
+
" setup() {",
|
|
341
|
+
` return ${setupReturn};`,
|
|
342
|
+
" },",
|
|
343
|
+
" render(__forgeVueCtx) {",
|
|
344
|
+
` return ${renderExpression};`,
|
|
345
|
+
" },",
|
|
346
|
+
"};",
|
|
347
|
+
"",
|
|
348
|
+
"export default __forgeVueComponent;",
|
|
349
|
+
"",
|
|
350
|
+
].join("\n");
|
|
295
351
|
}
|
|
296
|
-
function
|
|
352
|
+
function extractVueSfcBlock(source, blockName, requiredAttribute) {
|
|
353
|
+
const pattern = new RegExp(`<${blockName}\\b([^>]*)>([\\s\\S]*?)<\\/${blockName}>`, "gi");
|
|
354
|
+
for (const match of source.matchAll(pattern)) {
|
|
355
|
+
const attributes = match[1] ?? "";
|
|
356
|
+
if (requiredAttribute !== undefined &&
|
|
357
|
+
!new RegExp(`\\b${requiredAttribute}\\b`, "i").test(attributes)) {
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
return match[2].trim();
|
|
361
|
+
}
|
|
362
|
+
return undefined;
|
|
363
|
+
}
|
|
364
|
+
function collectVueSetupBindings(scriptSetup) {
|
|
365
|
+
const bindings = new Set();
|
|
366
|
+
for (const match of scriptSetup.matchAll(/\bimport\s+(type\s+)?([\s\S]*?)\s+from\s*["'][^"']+["']/g)) {
|
|
367
|
+
if (match[1] !== undefined) {
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
for (const binding of parseImportBindingNames(match[2])) {
|
|
371
|
+
bindings.add(binding);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
const declarationPatterns = [
|
|
375
|
+
/\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)/g,
|
|
376
|
+
/\bfunction\s+([A-Za-z_$][\w$]*)/g,
|
|
377
|
+
/\bclass\s+([A-Za-z_$][\w$]*)/g,
|
|
378
|
+
];
|
|
379
|
+
for (const pattern of declarationPatterns) {
|
|
380
|
+
for (const match of scriptSetup.matchAll(pattern)) {
|
|
381
|
+
bindings.add(match[1]);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
return Array.from(bindings).sort((left, right) => left.localeCompare(right));
|
|
385
|
+
}
|
|
386
|
+
function parseImportBindingNames(importClause) {
|
|
387
|
+
const bindings = [];
|
|
388
|
+
const trimmed = importClause.trim();
|
|
389
|
+
if (trimmed.length === 0 || trimmed.startsWith("{")) {
|
|
390
|
+
bindings.push(...parseNamedImportBindings(trimmed));
|
|
391
|
+
return bindings;
|
|
392
|
+
}
|
|
393
|
+
const [defaultOrNamespace = "", ...rest] = trimmed.split(",");
|
|
394
|
+
const defaultBinding = defaultOrNamespace.trim();
|
|
395
|
+
if (defaultBinding.startsWith("* as ")) {
|
|
396
|
+
bindings.push(defaultBinding.slice(5).trim());
|
|
397
|
+
}
|
|
398
|
+
else if (/^[A-Za-z_$][\w$]*$/.test(defaultBinding)) {
|
|
399
|
+
bindings.push(defaultBinding);
|
|
400
|
+
}
|
|
401
|
+
bindings.push(...parseNamedImportBindings(rest.join(",").trim()));
|
|
402
|
+
return bindings.filter((binding) => /^[A-Za-z_$][\w$]*$/.test(binding));
|
|
403
|
+
}
|
|
404
|
+
function parseNamedImportBindings(importClause) {
|
|
405
|
+
const namedMatch = importClause.match(/\{([\s\S]*?)\}/);
|
|
406
|
+
if (namedMatch === null) {
|
|
407
|
+
return [];
|
|
408
|
+
}
|
|
409
|
+
return namedMatch[1]
|
|
410
|
+
.split(",")
|
|
411
|
+
.map((part) => part.trim())
|
|
412
|
+
.filter((part) => part.length > 0 && !part.startsWith("type "))
|
|
413
|
+
.map((part) => {
|
|
414
|
+
const aliasMatch = part.match(/\bas\s+([A-Za-z_$][\w$]*)$/);
|
|
415
|
+
return aliasMatch?.[1] ?? part;
|
|
416
|
+
})
|
|
417
|
+
.filter((binding) => /^[A-Za-z_$][\w$]*$/.test(binding));
|
|
418
|
+
}
|
|
419
|
+
function createVueRenderExpression(template) {
|
|
420
|
+
const nodes = parseVueTemplate(template);
|
|
421
|
+
const renderedChildren = nodes.map(createVueTemplateNodeExpression);
|
|
422
|
+
if (renderedChildren.length === 0) {
|
|
423
|
+
return "null";
|
|
424
|
+
}
|
|
425
|
+
return renderedChildren.length === 1
|
|
426
|
+
? renderedChildren[0]
|
|
427
|
+
: `__forgeVueH("div", null, [${renderedChildren.join(", ")}])`;
|
|
428
|
+
}
|
|
429
|
+
function parseVueTemplate(template) {
|
|
430
|
+
const root = {
|
|
431
|
+
attributes: [],
|
|
432
|
+
children: [],
|
|
433
|
+
tagName: "__root__",
|
|
434
|
+
};
|
|
435
|
+
const stack = [root];
|
|
436
|
+
const tokenPattern = /<!--[\s\S]*?-->|<\/?[^>]+>|{{[\s\S]*?}}|[^<{]+|[<{]/g;
|
|
437
|
+
for (const match of template.matchAll(tokenPattern)) {
|
|
438
|
+
const token = match[0];
|
|
439
|
+
const current = stack[stack.length - 1];
|
|
440
|
+
if (token.startsWith("<!--")) {
|
|
441
|
+
continue;
|
|
442
|
+
}
|
|
443
|
+
if (token.startsWith("</")) {
|
|
444
|
+
if (stack.length > 1) {
|
|
445
|
+
stack.pop();
|
|
446
|
+
}
|
|
447
|
+
continue;
|
|
448
|
+
}
|
|
449
|
+
if (token.startsWith("<")) {
|
|
450
|
+
const element = parseVueTemplateElementToken(token);
|
|
451
|
+
if (element === undefined) {
|
|
452
|
+
continue;
|
|
453
|
+
}
|
|
454
|
+
current.children.push({
|
|
455
|
+
attributes: element.attributes,
|
|
456
|
+
children: element.children,
|
|
457
|
+
kind: "element",
|
|
458
|
+
tagName: element.tagName,
|
|
459
|
+
});
|
|
460
|
+
if (!isSelfClosingVueTemplateToken(token)) {
|
|
461
|
+
stack.push(element);
|
|
462
|
+
}
|
|
463
|
+
continue;
|
|
464
|
+
}
|
|
465
|
+
if (token.startsWith("{{")) {
|
|
466
|
+
current.children.push({
|
|
467
|
+
expression: token.slice(2, -2).trim(),
|
|
468
|
+
kind: "interpolation",
|
|
469
|
+
});
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
if (token.trim().length > 0) {
|
|
473
|
+
current.children.push({
|
|
474
|
+
kind: "text",
|
|
475
|
+
value: token.replace(/\s+/g, " "),
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
return root.children;
|
|
480
|
+
}
|
|
481
|
+
function parseVueTemplateElementToken(token) {
|
|
482
|
+
const content = token
|
|
483
|
+
.slice(1, isSelfClosingVueTemplateToken(token) ? -2 : -1)
|
|
484
|
+
.trim();
|
|
485
|
+
const tagMatch = content.match(/^([A-Za-z][\w.-]*)/);
|
|
486
|
+
if (tagMatch === null) {
|
|
487
|
+
return undefined;
|
|
488
|
+
}
|
|
489
|
+
return {
|
|
490
|
+
attributes: parseVueTemplateAttributes(content.slice(tagMatch[0].length)),
|
|
491
|
+
children: [],
|
|
492
|
+
tagName: tagMatch[1],
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
function parseVueTemplateAttributes(source) {
|
|
496
|
+
const attributes = [];
|
|
497
|
+
const pattern = /([:@A-Za-z_][\w:.-]*)(?:\s*=\s*("[^"]*"|'[^']*'|[^\s"'>/]+))?/g;
|
|
498
|
+
for (const match of source.matchAll(pattern)) {
|
|
499
|
+
const name = match[1];
|
|
500
|
+
if (name.startsWith(":") ||
|
|
501
|
+
name.startsWith("@") ||
|
|
502
|
+
name.startsWith("v-")) {
|
|
503
|
+
continue;
|
|
504
|
+
}
|
|
505
|
+
const rawValue = match[2];
|
|
506
|
+
const value = rawValue === undefined
|
|
507
|
+
? true
|
|
508
|
+
: rawValue.replace(/^["']|["']$/g, "");
|
|
509
|
+
attributes.push({ name, value });
|
|
510
|
+
}
|
|
511
|
+
return attributes;
|
|
512
|
+
}
|
|
513
|
+
function createVueTemplateNodeExpression(node) {
|
|
514
|
+
switch (node.kind) {
|
|
515
|
+
case "element":
|
|
516
|
+
return createVueElementExpression(node);
|
|
517
|
+
case "interpolation":
|
|
518
|
+
return createVueInterpolationExpression(node.expression);
|
|
519
|
+
case "text":
|
|
520
|
+
return JSON.stringify(node.value);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
function createVueElementExpression(node) {
|
|
524
|
+
const tagExpression = /^[A-Z]/.test(node.tagName)
|
|
525
|
+
? `(__forgeVueCtx[${JSON.stringify(node.tagName)}] ?? ${JSON.stringify(node.tagName)})`
|
|
526
|
+
: JSON.stringify(node.tagName);
|
|
527
|
+
const propsExpression = createVuePropsExpression(node.attributes);
|
|
528
|
+
const children = node.children.map(createVueTemplateNodeExpression);
|
|
529
|
+
const childrenExpression = children.length === 0
|
|
530
|
+
? "null"
|
|
531
|
+
: children.length === 1
|
|
532
|
+
? children[0]
|
|
533
|
+
: `[${children.join(", ")}]`;
|
|
534
|
+
return `__forgeVueH(${tagExpression}, ${propsExpression}, ${childrenExpression})`;
|
|
535
|
+
}
|
|
536
|
+
function createVuePropsExpression(attributes) {
|
|
537
|
+
if (attributes.length === 0) {
|
|
538
|
+
return "null";
|
|
539
|
+
}
|
|
540
|
+
return `{ ${attributes
|
|
541
|
+
.map((attribute) => {
|
|
542
|
+
const value = attribute.value === true ? "true" : JSON.stringify(attribute.value);
|
|
543
|
+
return `${JSON.stringify(attribute.name)}: ${value}`;
|
|
544
|
+
})
|
|
545
|
+
.join(", ")} }`;
|
|
546
|
+
}
|
|
547
|
+
function createVueInterpolationExpression(expression) {
|
|
548
|
+
if (/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*$/.test(expression)) {
|
|
549
|
+
return `String(__forgeVueCtx.${expression} ?? "")`;
|
|
550
|
+
}
|
|
551
|
+
return "String(\"\")";
|
|
552
|
+
}
|
|
553
|
+
function isSelfClosingVueTemplateToken(token) {
|
|
554
|
+
return /\/\s*>$/.test(token);
|
|
555
|
+
}
|
|
556
|
+
function createWorkspaceCssModule(sourcePath, css) {
|
|
557
|
+
return isCssModulePath(sourcePath)
|
|
558
|
+
? createCssModule(sourcePath, css)
|
|
559
|
+
: createPlainCssModule(sourcePath, css);
|
|
560
|
+
}
|
|
561
|
+
function createPlainCssModule(sourcePath, css) {
|
|
297
562
|
return [
|
|
298
563
|
`const css = ${JSON.stringify(css)};`,
|
|
299
564
|
`const id = ${JSON.stringify(`forge-css:${sourcePath}`)};`,
|
|
@@ -306,6 +571,70 @@ function createCssModule(sourcePath, css) {
|
|
|
306
571
|
"",
|
|
307
572
|
].join("\n");
|
|
308
573
|
}
|
|
574
|
+
function createCssModule(sourcePath, css) {
|
|
575
|
+
const protectedCss = protectCssModuleGlobalSelectors(css);
|
|
576
|
+
const classMap = createCssModuleClassMap(sourcePath, protectedCss.css);
|
|
577
|
+
const rewrittenCss = restoreCssModuleGlobalSelectors(rewriteCssModuleClassSelectors(protectedCss.css, classMap), protectedCss.globalSelectors);
|
|
578
|
+
return [
|
|
579
|
+
`const css = ${JSON.stringify(rewrittenCss)};`,
|
|
580
|
+
`const id = ${JSON.stringify(`forge-css:${sourcePath}`)};`,
|
|
581
|
+
"const previous = document.querySelector(`style[data-forge-css=\"${id}\"]`);",
|
|
582
|
+
"const style = previous ?? document.createElement('style');",
|
|
583
|
+
"style.setAttribute('data-forge-css', id);",
|
|
584
|
+
"style.textContent = css;",
|
|
585
|
+
"if (previous === null) document.head.append(style);",
|
|
586
|
+
`const classes = ${JSON.stringify(classMap, null, 2)};`,
|
|
587
|
+
"export default classes;",
|
|
588
|
+
"",
|
|
589
|
+
].join("\n");
|
|
590
|
+
}
|
|
591
|
+
function protectCssModuleGlobalSelectors(css) {
|
|
592
|
+
const globalSelectors = [];
|
|
593
|
+
const protectedCss = css.replace(/:global\(([^()]*)\)/g, (_match, selector) => {
|
|
594
|
+
const placeholder = `/*__forge_css_module_global_${globalSelectors.length}__*/`;
|
|
595
|
+
globalSelectors.push({ placeholder, selector });
|
|
596
|
+
return placeholder;
|
|
597
|
+
});
|
|
598
|
+
return { css: protectedCss, globalSelectors };
|
|
599
|
+
}
|
|
600
|
+
function restoreCssModuleGlobalSelectors(css, globalSelectors) {
|
|
601
|
+
return globalSelectors.reduce((currentCss, globalSelector) => currentCss
|
|
602
|
+
.split(globalSelector.placeholder)
|
|
603
|
+
.join(globalSelector.selector), css);
|
|
604
|
+
}
|
|
605
|
+
function createCssModuleClassMap(sourcePath, css) {
|
|
606
|
+
const classNames = new Set();
|
|
607
|
+
for (const match of css.matchAll(/\.([A-Za-z_][\w-]*)/g)) {
|
|
608
|
+
classNames.add(match[1]);
|
|
609
|
+
}
|
|
610
|
+
return Object.fromEntries(Array.from(classNames)
|
|
611
|
+
.sort((left, right) => left.localeCompare(right))
|
|
612
|
+
.map((className) => [
|
|
613
|
+
className,
|
|
614
|
+
createCssModuleScopedClassName(sourcePath, className),
|
|
615
|
+
]));
|
|
616
|
+
}
|
|
617
|
+
function rewriteCssModuleClassSelectors(css, classMap) {
|
|
618
|
+
return css.replace(/(^|[\s,{>+~])\.([A-Za-z_][\w-]*)/g, (match, prefix, className) => {
|
|
619
|
+
const scopedClassName = classMap[className];
|
|
620
|
+
return scopedClassName === undefined
|
|
621
|
+
? match
|
|
622
|
+
: `${prefix}.${scopedClassName}`;
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
function createCssModuleScopedClassName(sourcePath, className) {
|
|
626
|
+
const pathParts = sourcePath.split("/");
|
|
627
|
+
const fileName = pathParts[pathParts.length - 1] ?? "style.module.css";
|
|
628
|
+
const baseName = fileName
|
|
629
|
+
.replace(/\.module\.css$/i, "")
|
|
630
|
+
.replace(/[^a-z0-9_-]+/gi, "_");
|
|
631
|
+
const safeClassName = className.replace(/[^a-z0-9_-]+/gi, "_");
|
|
632
|
+
const hash = hashText(`${sourcePath}:${className}`).slice(0, 8);
|
|
633
|
+
return `_${baseName}_${safeClassName}_${hash}`;
|
|
634
|
+
}
|
|
635
|
+
function isCssModulePath(path) {
|
|
636
|
+
return /\.module\.css$/i.test(path);
|
|
637
|
+
}
|
|
309
638
|
function createJsonModule(json) {
|
|
310
639
|
return `export default ${json.trim()};\n`;
|
|
311
640
|
}
|
|
@@ -346,6 +675,31 @@ function extractImportReferences(source) {
|
|
|
346
675
|
}
|
|
347
676
|
return references;
|
|
348
677
|
}
|
|
678
|
+
function resolveAliasSpecifier(specifier, aliases) {
|
|
679
|
+
for (const [find, replacement] of Object.entries(aliases).sort(([left], [right]) => right.length - left.length || left.localeCompare(right))) {
|
|
680
|
+
if (specifier === find) {
|
|
681
|
+
return replacement;
|
|
682
|
+
}
|
|
683
|
+
if (specifier.startsWith(`${find}/`)) {
|
|
684
|
+
const normalizedReplacement = replacement.endsWith("/")
|
|
685
|
+
? replacement.slice(0, -1)
|
|
686
|
+
: replacement;
|
|
687
|
+
return `${normalizedReplacement}${specifier.slice(find.length)}`;
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
return undefined;
|
|
691
|
+
}
|
|
692
|
+
function applyDefineReplacements(source, defineReplacements) {
|
|
693
|
+
let output = source;
|
|
694
|
+
for (const [key, value] of Object.entries(defineReplacements).sort(([left], [right]) => right.length - left.length || left.localeCompare(right))) {
|
|
695
|
+
if (!isSupportedDefineKey(key)) {
|
|
696
|
+
continue;
|
|
697
|
+
}
|
|
698
|
+
const pattern = new RegExp(`(^|[^A-Za-z0-9_$.])${escapeRegExp(key)}(?![A-Za-z0-9_$])`, "g");
|
|
699
|
+
output = output.replace(pattern, (_match, prefix) => `${prefix}${value}`);
|
|
700
|
+
}
|
|
701
|
+
return output;
|
|
702
|
+
}
|
|
349
703
|
async function resolveLocalImport(fileSystem, importer, specifier) {
|
|
350
704
|
const basePath = specifier.startsWith("/")
|
|
351
705
|
? specifier
|
|
@@ -517,6 +871,12 @@ function isLocalSpecifier(specifier) {
|
|
|
517
871
|
function isExternalUrl(specifier) {
|
|
518
872
|
return /^https?:\/\//i.test(specifier) || specifier.startsWith("data:");
|
|
519
873
|
}
|
|
874
|
+
function isSupportedDefineKey(key) {
|
|
875
|
+
return /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*$/.test(key);
|
|
876
|
+
}
|
|
877
|
+
function escapeRegExp(value) {
|
|
878
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
879
|
+
}
|
|
520
880
|
function dirname(path) {
|
|
521
881
|
const index = path.lastIndexOf("/");
|
|
522
882
|
return index <= 0 ? "/" : path.slice(0, index);
|
|
@@ -64,7 +64,7 @@ function createViteConfigNotExecutedDiagnostic(path) {
|
|
|
64
64
|
code: "vite_config_not_executed",
|
|
65
65
|
severity: "warning",
|
|
66
66
|
message: `Forge detected ${path} but did not execute it. ` +
|
|
67
|
-
"
|
|
67
|
+
"Only static aliases, defines, and base are parsed; plugins and dynamic config are ignored.",
|
|
68
68
|
path,
|
|
69
69
|
};
|
|
70
70
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { createBuildCancelledDiagnostic, createEsbuildWorkerTransformDiagnostic, createMissingBuildScriptDiagnostic, createMissingEntryDiagnostic, createUnresolvedImportDiagnostic, createUnsupportedBuildScriptDiagnostic, createViteConfigNotExecutedDiagnostic, } from "./diagnostics";
|
|
2
2
|
export { createBuildPipeline, DefaultBuildPipeline, isSupportedViteBuildScript, } from "./pipeline";
|
|
3
|
-
export { BUILD_PIPELINE_CAPABILITY, type ForgeBrowserBundle, type ForgeBrowserBundleManifest, type ForgeBuildDiagnostic, type ForgeBuildManifest, type ForgeBuildMode, type ForgeBuildOutputFile, type ForgeBuildPipeline, type ForgeBuildPipelineDiagnostic, type ForgeBuildPipelineDiagnosticCode, type ForgeBuildPipelineOptions, type ForgeBuildRequest, type ForgeBuildResult, type ForgeBuildTiming, } from "./types";
|
|
3
|
+
export { BUILD_PIPELINE_CAPABILITY, type ForgeBrowserBundle, type ForgeBrowserBundleManifest, type ForgeBrowserBundleModule, type ForgeBrowserBundleModuleOrigin, type ForgeBuildDiagnostic, type ForgeBuildManifest, type ForgeBuildMode, type ForgeBuildOutputFile, type ForgeBuildPipeline, type ForgeBuildPipelineDiagnostic, type ForgeBuildPipelineDiagnosticCode, type ForgeBuildPipelineOptions, type ForgeBuildRequest, type ForgeBuildResult, type ForgeBuildTiming, type ForgeViteConfigMetadata, } from "./types";
|