@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.
@@ -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 imports = extractImportReferences(source).map((reference) => reference.specifier);
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: modules.size + packageModules.size,
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
- async function transformWorkspaceModule(sourcePath, source, scriptTransformer) {
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(createCssModule(sourcePath, source));
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
- return scriptTransformer({
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(ts.transpileModule(source, {
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(createCssModule(importer, source));
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(ts.transpileModule(source, {
283
- compilerOptions: {
284
- esModuleInterop: true,
285
- importsNotUsedAsValues: ts.ImportsNotUsedAsValues.Remove,
286
- jsx: ts.JsxEmit.React,
287
- jsxFactory: "React.createElement",
288
- jsxFragmentFactory: "React.Fragment",
289
- module: ts.ModuleKind.ESNext,
290
- target: ts.ScriptTarget.ES2020,
291
- },
292
- fileName: importer,
293
- reportDiagnostics: false,
294
- }).outputText);
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 createCssModule(sourcePath, css) {
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
- "Config-driven aliases, plugins, defines, and server options are ignored.",
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";