@blotch/rsbuild-plugin-angular-lynx 0.0.0-stage → 22.2.1

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.
Files changed (76) hide show
  1. package/README.md +37 -2
  2. package/dist/angular-rebuild-coordinator.d.ts +70 -0
  3. package/dist/angular-rebuild-coordinator.test.d.ts +1 -0
  4. package/dist/angular-webpack-plugin.d.ts +127 -0
  5. package/dist/angular-webpack-plugin.test.d.ts +1 -0
  6. package/dist/angular.d.ts +31 -0
  7. package/dist/build-schema-source-file-cache.d.ts +20 -0
  8. package/dist/build-schema-source-file-cache.test.d.ts +1 -0
  9. package/dist/component-styles-cache.d.ts +43 -0
  10. package/dist/component-styles-cache.test.d.ts +1 -0
  11. package/dist/css.d.ts +13 -0
  12. package/dist/css.test.d.ts +1 -0
  13. package/dist/entry.d.ts +11 -0
  14. package/dist/entry.test.d.ts +1 -0
  15. package/dist/generator.d.ts +2 -0
  16. package/dist/generator.test.d.ts +1 -0
  17. package/dist/index.d.ts +1 -0
  18. package/dist/index.js +1510 -0
  19. package/dist/index.js.LICENSE.txt +7 -0
  20. package/dist/is-lynx-unknown-element-message.d.ts +12 -0
  21. package/dist/is-lynx-unknown-element-message.test.d.ts +1 -0
  22. package/dist/layers.d.ts +12 -0
  23. package/dist/layers.test.d.ts +1 -0
  24. package/dist/loaders/ignore-css-loader.d.ts +7 -0
  25. package/dist/loaders/ignore-css-loader.js +7 -0
  26. package/dist/loaders/ignore-css-loader.test.d.ts +1 -0
  27. package/dist/loaders/thread-globals-loader.d.ts +8 -0
  28. package/dist/loaders/thread-globals-loader.js +6 -0
  29. package/dist/loaders/thread-globals-loader.test.d.ts +1 -0
  30. package/dist/logger/dev-logger-middleware.d.ts +12 -0
  31. package/dist/logger/dev-logger-middleware.test.d.ts +1 -0
  32. package/dist/logger/dev-logger.d.ts +2 -0
  33. package/dist/logger/dev-logger.test.d.ts +1 -0
  34. package/dist/lynx-diagnostics.d.ts +38 -0
  35. package/dist/lynx-diagnostics.test.d.ts +1 -0
  36. package/dist/lynx-process-eval-result-runtime-module.d.ts +21 -0
  37. package/dist/lynx-process-eval-result-runtime-module.test.d.ts +1 -0
  38. package/dist/plugin-angular-lynx.d.ts +3 -0
  39. package/dist/plugin-angular-lynx.test.d.ts +1 -0
  40. package/dist/polyfills.js +122 -0
  41. package/dist/seed-compilation-source-files.d.ts +22 -0
  42. package/dist/seed-compilation-source-files.test.d.ts +1 -0
  43. package/dist/split-chunks.d.ts +3 -0
  44. package/dist/split-chunks.test.d.ts +1 -0
  45. package/dist/tailwind.d.ts +2 -0
  46. package/dist/tailwind.test.d.ts +1 -0
  47. package/dist/transform-module.d.ts +31 -0
  48. package/dist/transform-module.test.d.ts +1 -0
  49. package/dist/utils/angular/angular-config.d.ts +3 -0
  50. package/dist/utils/angular/angular-config.test.d.ts +1 -0
  51. package/dist/utils/angular/component-scope-id.d.ts +12 -0
  52. package/dist/utils/angular/component-scope-id.test.d.ts +1 -0
  53. package/dist/utils/angular/component-style-bundler.d.ts +19 -0
  54. package/dist/utils/angular/component-style-bundler.test.d.ts +1 -0
  55. package/dist/utils/angular/env.d.ts +2 -0
  56. package/dist/utils/angular/env.test.d.ts +1 -0
  57. package/dist/utils/angular/find-up.d.ts +8 -0
  58. package/dist/utils/angular/find-up.test.d.ts +1 -0
  59. package/dist/utils/angular/normalize-cache.d.ts +22 -0
  60. package/dist/utils/angular/normalize-cache.test.d.ts +1 -0
  61. package/dist/utils/angular/options.d.ts +82 -0
  62. package/dist/utils/angular/options.test.d.ts +1 -0
  63. package/dist/utils/angular/read-workspace.d.ts +7 -0
  64. package/dist/utils/angular/read-workspace.test.d.ts +1 -0
  65. package/dist/utils/angular/resolve-pages.d.ts +13 -0
  66. package/dist/utils/angular/resolve-pages.test.d.ts +1 -0
  67. package/dist/utils/inject-lynx-schema.d.ts +10 -0
  68. package/dist/utils/inject-lynx-schema.test.d.ts +1 -0
  69. package/dist/utils/options.d.ts +31 -0
  70. package/dist/utils/options.test.d.ts +1 -0
  71. package/dist/utils/strip-template-whitespace.d.ts +34 -0
  72. package/dist/utils/strip-template-whitespace.test.d.ts +1 -0
  73. package/dist/worklet-transform.d.ts +7 -0
  74. package/dist/worklet-transform.test.d.ts +1 -0
  75. package/package.json +73 -4
  76. package/patch/patch-angular-build.cjs +27 -0
package/dist/index.js ADDED
@@ -0,0 +1,1510 @@
1
+ /*! LICENSE: index.js.LICENSE.txt */
2
+ import node_fs, { existsSync } from "node:fs";
3
+ import node_path, { join, resolve } from "node:path";
4
+ import { DiagnosticModes, createAngularCompilation } from "@angular/build/src/tools/angular/compilation";
5
+ import { JavaScriptTransformer } from "@angular/build/src/tools/javascript-transformer";
6
+ import { createHash } from "node:crypto";
7
+ import { fileURLToPath } from "node:url";
8
+ import { availableParallelism } from "node:os";
9
+ import node_assert from "node:assert";
10
+ import { access, constants } from "node:fs/promises";
11
+ import { workspaces } from "@angular-devkit/core";
12
+ import { NodeJsSyncHost } from "@angular-devkit/core/node";
13
+ import { CSSPlugins, LynxEncodePlugin, LynxTemplatePlugin, WebEncodePlugin } from "@lynx-js/template-webpack-plugin";
14
+ import { RuntimeWrapperWebpackPlugin } from "@lynx-js/runtime-wrapper-webpack-plugin";
15
+ import { RuntimeGlobals } from "@lynx-js/webpack-runtime-globals";
16
+ import tiny_invariant from "tiny-invariant";
17
+ import node_http from "node:http";
18
+ import { pluginTailwindCSS } from "rsbuild-plugin-tailwindcss";
19
+ import * as __rspack_external_typescript from "typescript";
20
+ import * as __rspack_external_node_path_c5b9b54f from "node:path";
21
+ const TYPESCRIPT_FILE = /\.[cm]?tsx?$/;
22
+ const readFileVersion = (file)=>node_fs.statSync(file, {
23
+ throwIfNoEntry: false
24
+ })?.mtimeMs ?? null;
25
+ const createAngularRebuildCoordinator = ({ compile, getFileVersion = readFileVersion })=>{
26
+ let queue = Promise.resolve();
27
+ let initialized = false;
28
+ let inputFiles = new Set();
29
+ const pendingFiles = new Set();
30
+ const compiledVersions = new Map();
31
+ let generation = 0;
32
+ const outputGenerations = new Map();
33
+ const environmentGenerations = new Map();
34
+ const isAngularInput = (file)=>TYPESCRIPT_FILE.test(file) || inputFiles.has(file);
35
+ const runCompile = async ()=>{
36
+ if (initialized && 0 === pendingFiles.size) return;
37
+ const changedFiles = new Set(pendingFiles);
38
+ pendingFiles.clear();
39
+ let result;
40
+ try {
41
+ result = await compile(initialized ? changedFiles : void 0);
42
+ } catch (error) {
43
+ for (const file of changedFiles){
44
+ pendingFiles.add(file);
45
+ compiledVersions.delete(file);
46
+ }
47
+ throw error;
48
+ }
49
+ inputFiles = new Set(result.inputFiles);
50
+ if (!initialized) {
51
+ initialized = true;
52
+ return;
53
+ }
54
+ generation++;
55
+ for (const file of result.changedOutputs)outputGenerations.set(file, generation);
56
+ };
57
+ return {
58
+ prepare: async (environmentName, changedFiles)=>{
59
+ for (const file of changedFiles){
60
+ if (!isAngularInput(file)) continue;
61
+ const version = getFileVersion(file);
62
+ if (!compiledVersions.has(file) || compiledVersions.get(file) !== version) {
63
+ compiledVersions.set(file, version);
64
+ pendingFiles.add(file);
65
+ }
66
+ }
67
+ const run = queue.then(runCompile);
68
+ queue = run.catch(()=>{});
69
+ await run;
70
+ const lastSeen = environmentGenerations.get(environmentName);
71
+ environmentGenerations.set(environmentName, generation);
72
+ const outdated = new Set();
73
+ if (void 0 === lastSeen) return outdated;
74
+ for (const [file, outputGeneration] of outputGenerations)if (outputGeneration > lastSeen) outdated.add(file);
75
+ return outdated;
76
+ }
77
+ };
78
+ };
79
+ const injectLynxSchema = (source)=>{
80
+ if (source.includes('CUSTOM_ELEMENTS_SCHEMA') || source.includes('NO_ERRORS_SCHEMA')) return source;
81
+ const importLine = `import { CUSTOM_ELEMENTS_SCHEMA as __LynxCES__ } from '@angular/core';\n`;
82
+ const patched = source.replace(/@Component\s*\(\s*\{/g, '@Component({ schemas: [__LynxCES__],');
83
+ if (patched === source) return source;
84
+ return importLine + patched;
85
+ };
86
+ const stripTemplateWhitespace = (source)=>source.replace(/(?<=>)[ \t]*\n[ \t]*([^\n<]+?)[ \t]*\n[ \t]*(?=<)/g, '$1');
87
+ const buildLynxSchemaSourceFileCache = (tsconfig, shouldInclude = ()=>true)=>{
88
+ const sourceFileCache = new Map();
89
+ let fileNames;
90
+ try {
91
+ const configFile = __rspack_external_typescript.readConfigFile(tsconfig, (p)=>node_fs.readFileSync(p, 'utf-8'));
92
+ const parsedConfig = __rspack_external_typescript.parseJsonConfigFileContent(configFile.config, __rspack_external_typescript.sys, node_path.dirname(tsconfig));
93
+ fileNames = parsedConfig.fileNames;
94
+ } catch {
95
+ return {
96
+ sourceFileCache,
97
+ fileNames: []
98
+ };
99
+ }
100
+ for (const filePath of fileNames){
101
+ if (filePath.includes('node_modules')) continue;
102
+ if (!shouldInclude(filePath)) continue;
103
+ let source;
104
+ try {
105
+ source = node_fs.readFileSync(filePath, 'utf-8');
106
+ } catch {
107
+ continue;
108
+ }
109
+ if (!source.includes('@Component')) continue;
110
+ const transformed = stripTemplateWhitespace(injectLynxSchema(source));
111
+ sourceFileCache.set(filePath, __rspack_external_typescript.createSourceFile(filePath, transformed, __rspack_external_typescript.ScriptTarget.Latest, true));
112
+ }
113
+ return {
114
+ sourceFileCache,
115
+ fileNames
116
+ };
117
+ };
118
+ const generateComponentScopeId = (className, containingFile)=>{
119
+ const hash = createHash('sha256').update(`${className}|${containingFile}`).digest('hex').slice(0, 10);
120
+ return `l${hash}`;
121
+ };
122
+ const createTransformStylesheet = (params)=>{
123
+ const { basePath, scopedCssCacheDir, componentStylesCache, componentScopeIds, onStylesheetWritten } = params;
124
+ return async (data, containingFile, stylesheetFile, order, className)=>{
125
+ const resolvedClassName = className ?? 'Component';
126
+ const scopeId = generateComponentScopeId(resolvedClassName, containingFile);
127
+ let componentStyles = componentStylesCache.get(containingFile);
128
+ if (!componentStyles) {
129
+ componentStyles = {
130
+ imports: [],
131
+ inlineStyles: [],
132
+ scopeId,
133
+ processedFiles: new Map()
134
+ };
135
+ componentStylesCache.set(containingFile, componentStyles);
136
+ }
137
+ const stylesheetKey = stylesheetFile ?? `__inline_${order}`;
138
+ const previousPath = componentStyles.processedFiles.get(stylesheetKey);
139
+ if (previousPath && !className) return '';
140
+ if (className || !componentScopeIds.has(containingFile)) componentScopeIds.set(containingFile, {
141
+ className: resolvedClassName,
142
+ scopeId
143
+ });
144
+ const scopedCss = data;
145
+ const writeIfChanged = (filePath, content)=>{
146
+ try {
147
+ if (node_fs.readFileSync(filePath, 'utf-8') === content) return;
148
+ } catch {}
149
+ node_fs.writeFileSync(filePath, content);
150
+ onStylesheetWritten?.(filePath);
151
+ };
152
+ let scopedPath;
153
+ if (stylesheetFile) {
154
+ const relName = node_path.relative(basePath, stylesheetFile);
155
+ scopedPath = node_path.join(scopedCssCacheDir, `${relName.replace(/[/\\]/g, '__')}.__scoped_${scopeId}.css`);
156
+ } else {
157
+ const relDir = node_path.relative(basePath, node_path.dirname(containingFile));
158
+ scopedPath = node_path.join(scopedCssCacheDir, `${relDir.replace(/[/\\]/g, '__')}__inline_${resolvedClassName}_${order}.__scoped_${scopeId}.css`);
159
+ }
160
+ writeIfChanged(scopedPath, scopedCss);
161
+ if (previousPath) {
162
+ const idx = componentStyles.imports.indexOf(previousPath);
163
+ if (idx >= 0) componentStyles.imports[idx] = scopedPath;
164
+ else componentStyles.imports.push(scopedPath);
165
+ if (previousPath !== scopedPath) try {
166
+ node_fs.unlinkSync(previousPath);
167
+ } catch {}
168
+ } else componentStyles.imports.push(scopedPath);
169
+ componentStyles.processedFiles.set(stylesheetKey, scopedPath);
170
+ return '';
171
+ };
172
+ };
173
+ const isLynxUnknownElementMessage = (text)=>(text?.includes('is not a known element') || text?.includes("isn't a known property of")) ?? false;
174
+ const seedCompilationSourceFiles = (compilation, sourceFileCache)=>{
175
+ const { sourceFiles } = compilation;
176
+ if (!(sourceFiles instanceof Map)) return false;
177
+ for (const [fileName, sourceFile] of sourceFileCache)sourceFiles.set(fileName, sourceFile);
178
+ return true;
179
+ };
180
+ const MAIN_THREAD_DIRECTIVE = 'main thread';
181
+ const MAIN_THREAD_DIRECTIVE_ALT = 'main-thread';
182
+ const transformWorklets = (code, filename)=>{
183
+ if (!code.includes(MAIN_THREAD_DIRECTIVE) && !code.includes(MAIN_THREAD_DIRECTIVE_ALT)) return code;
184
+ const sourceFile = __rspack_external_typescript.createSourceFile(filename, code, __rspack_external_typescript.ScriptTarget.Latest, true);
185
+ const workletRanges = [];
186
+ visitNode(sourceFile, sourceFile, workletRanges);
187
+ if (0 === workletRanges.length) return code;
188
+ let result = code;
189
+ for(let i = workletRanges.length - 1; i >= 0; i--){
190
+ const { start, end } = workletRanges[i];
191
+ const fnText = result.slice(start, end);
192
+ result = result.slice(0, start) + `mainThreadFn(${fnText})` + result.slice(end);
193
+ }
194
+ result = `import { mainThreadFn } from '@blotch/angular-lynx';\n` + result;
195
+ return result;
196
+ };
197
+ const visitNode = (node, sourceFile, ranges)=>{
198
+ if (__rspack_external_typescript.isArrowFunction(node) || __rspack_external_typescript.isFunctionExpression(node)) {
199
+ if (hasMainThreadDirective(node) && !isAlreadyWrapped(node)) return void ranges.push({
200
+ start: node.getStart(sourceFile),
201
+ end: node.getEnd()
202
+ });
203
+ }
204
+ __rspack_external_typescript.forEachChild(node, (child)=>visitNode(child, sourceFile, ranges));
205
+ };
206
+ const hasMainThreadDirective = (node)=>{
207
+ const body = node.body;
208
+ if (!__rspack_external_typescript.isBlock(body)) return false;
209
+ if (0 === body.statements.length) return false;
210
+ const first = body.statements[0];
211
+ if (!__rspack_external_typescript.isExpressionStatement(first)) return false;
212
+ const expr = first.expression;
213
+ if (!__rspack_external_typescript.isStringLiteral(expr)) return false;
214
+ const text = expr.text;
215
+ return text === MAIN_THREAD_DIRECTIVE || text === MAIN_THREAD_DIRECTIVE_ALT;
216
+ };
217
+ const isAlreadyWrapped = (node)=>{
218
+ const parent = node.parent;
219
+ if (!parent || !__rspack_external_typescript.isCallExpression(parent)) return false;
220
+ const callee = parent.expression;
221
+ if (__rspack_external_typescript.isIdentifier(callee) && 'mainThreadFn' === callee.text) return true;
222
+ if (__rspack_external_typescript.isPropertyAccessExpression(callee) && 'mainThreadFn' === callee.name.text) return true;
223
+ return false;
224
+ };
225
+ const buildTransformedCode = (params)=>{
226
+ const { resourcePath, componentStyles, scopeInfo } = params;
227
+ let code = params.code;
228
+ if (componentStyles) {
229
+ const { imports } = componentStyles;
230
+ let importsString = '';
231
+ for(let i = 0; i < imports.length; ++i){
232
+ let relativeImport = node_path.relative(node_path.dirname(resourcePath), imports[i]);
233
+ if (!relativeImport.startsWith('.')) relativeImport = `./${relativeImport}`;
234
+ importsString += `import "${relativeImport}";`;
235
+ }
236
+ code = importsString + code;
237
+ }
238
+ if (scopeInfo) code += `\n;${scopeInfo.className}.ɵcmp.id = '${scopeInfo.scopeId}';\n`;
239
+ return transformWorklets(code, resourcePath);
240
+ };
241
+ const applyAngularConfig = (api, buildOptions)=>{
242
+ const __dirname = node_path.dirname(fileURLToPath(import.meta.url));
243
+ api.modifyRsbuildConfig((config)=>{
244
+ config.output = config.output ?? {};
245
+ if (buildOptions.outputPath) config.output.distPath = {
246
+ ...'object' == typeof config.output.distPath ? config.output.distPath : {},
247
+ root: buildOptions.outputPath
248
+ };
249
+ config.output.cleanDistPath = true;
250
+ config.source ??= {};
251
+ if ('string' == typeof config.source.preEntry) config.source.preEntry = [
252
+ config.source.preEntry
253
+ ];
254
+ else config.source.preEntry ??= [];
255
+ config.source.preEntry.push(node_path.resolve(__dirname, './polyfills'));
256
+ if (buildOptions.i18n.hasDefinedSourceLocale) config.source.preEntry.push('@angular/localize/init');
257
+ const polyfills = buildOptions.polyfills;
258
+ if (polyfills) config.source.preEntry.push(...polyfills);
259
+ config.source.preEntry.push(...buildOptions.styles);
260
+ config.source.tsconfigPath = buildOptions.tsconfig;
261
+ config.source.define ??= {};
262
+ config.source.define['__LYNX_SOURCE_LOCALE__'] = JSON.stringify(buildOptions.i18n.sourceLocale);
263
+ const isProd = 'production' === process.env.NODE_ENV || 'production' === config.mode;
264
+ if (isProd) config.source.define.ngDevMode = false;
265
+ });
266
+ };
267
+ const isPresent = (variable)=>'string' == typeof variable && '' !== variable;
268
+ const isDisabled = (variable)=>'0' === variable || 'false' === variable.toLowerCase();
269
+ const maxWorkersVariable = process.env.NG_BUILD_MAX_WORKERS;
270
+ const maxWorkers = isPresent(maxWorkersVariable) ? +maxWorkersVariable : Math.min(4, Math.max(availableParallelism() - 1, 1));
271
+ const typeCheckingVariable = process.env.NG_BUILD_TYPE_CHECK;
272
+ const useTypeChecking = !isPresent(typeCheckingVariable) || !isDisabled(typeCheckingVariable);
273
+ /**
274
+ * @license
275
+ * Copyright Google LLC All Rights Reserved.
276
+ *
277
+ * Use of this source code is governed by an MIT-style license that can be
278
+ * found in the LICENSE file at https://angular.dev/license
279
+ */ const VERSION = '0.0.0-PLACEHOLDER';
280
+ const hasCacheMetadata = (value)=>!!value && 'object' == typeof value && 'cli' in value && !!value.cli && 'object' == typeof value.cli && 'cache' in value.cli;
281
+ const normalizeCacheOptions = (projectMetadata, workspaceRoot)=>{
282
+ const cacheMetadata = hasCacheMetadata(projectMetadata) ? projectMetadata.cli.cache : {};
283
+ const { enabled = !process.versions.webcontainer, environment = 'local', path = '.angular/cache' } = cacheMetadata;
284
+ const isCI = '1' === process.env.CI || process.env.CI?.toLowerCase() === 'true';
285
+ let cacheEnabled = enabled;
286
+ if (cacheEnabled) switch(environment){
287
+ case 'ci':
288
+ cacheEnabled = isCI;
289
+ break;
290
+ case 'local':
291
+ cacheEnabled = !isCI;
292
+ break;
293
+ }
294
+ const cacheBasePath = resolve(workspaceRoot, path);
295
+ return {
296
+ enabled: cacheEnabled,
297
+ basePath: cacheBasePath,
298
+ path: join(cacheBasePath, VERSION)
299
+ };
300
+ };
301
+ const normalizeSourceMaps = (sourceMap)=>{
302
+ const scripts = 'object' == typeof sourceMap ? sourceMap.scripts : sourceMap;
303
+ const styles = 'object' == typeof sourceMap ? sourceMap.styles : sourceMap;
304
+ const hidden = 'object' == typeof sourceMap && sourceMap.hidden || false;
305
+ const vendor = 'object' == typeof sourceMap && sourceMap.vendor || false;
306
+ return {
307
+ vendor,
308
+ hidden,
309
+ scripts,
310
+ styles
311
+ };
312
+ };
313
+ const normalizeOptimization = (optimization = true)=>{
314
+ if ('object' == typeof optimization) {
315
+ const styleOptimization = !!optimization.styles;
316
+ return {
317
+ scripts: !!optimization.scripts,
318
+ styles: 'object' == typeof optimization.styles ? optimization.styles : {
319
+ minify: styleOptimization,
320
+ removeSpecialComments: styleOptimization,
321
+ inlineCritical: styleOptimization
322
+ },
323
+ fonts: 'object' == typeof optimization.fonts ? optimization.fonts : {
324
+ inline: !!optimization.fonts
325
+ }
326
+ };
327
+ }
328
+ return {
329
+ scripts: optimization,
330
+ styles: {
331
+ minify: optimization,
332
+ inlineCritical: optimization,
333
+ removeSpecialComments: optimization
334
+ },
335
+ fonts: {
336
+ inline: optimization
337
+ }
338
+ };
339
+ };
340
+ const readTarget = (project, target = 'build')=>project.targets.get(target);
341
+ const readBuildOptions = async (project, basePath, configurationName)=>{
342
+ const workspaceRoot = basePath;
343
+ const target = readTarget(project);
344
+ if (!target) throw new Error("couldn't find target");
345
+ const resolvedConfigurationName = configurationName ?? target.defaultConfiguration;
346
+ const buildOptions = target?.options;
347
+ if (!buildOptions) throw new Error(`No build options found for the "${configurationName ?? 'build'}" target in angular.json. Make sure the target has an "options" section with at least "browser", "tsConfig", and "index" defined.`);
348
+ const configurations = target.configurations;
349
+ if (configurations) {
350
+ for(const name in configurations)if (name === resolvedConfigurationName) {
351
+ Object.assign(buildOptions, {
352
+ ...configurations[name]
353
+ });
354
+ break;
355
+ }
356
+ }
357
+ node_assert(!!buildOptions, 'build options is undefined');
358
+ const aot = buildOptions.aot ?? true;
359
+ const tsconfig = node_path.join(workspaceRoot, buildOptions.tsConfig);
360
+ const optimizationOptions = normalizeOptimization(buildOptions.optimization);
361
+ const sourcemapOptions = normalizeSourceMaps(buildOptions.sourceMap ?? false);
362
+ const browser = node_path.join(workspaceRoot, buildOptions.browser);
363
+ const index = node_path.join(workspaceRoot, buildOptions.index);
364
+ let polyfills = buildOptions.polyfills;
365
+ polyfills = void 0 === polyfills || Array.isArray(polyfills) ? polyfills : [
366
+ polyfills
367
+ ];
368
+ let fileReplacements;
369
+ const buildOptionsFileReplacements = buildOptions.fileReplacements;
370
+ if (buildOptionsFileReplacements) for (const replacement of buildOptionsFileReplacements){
371
+ const fileReplaceWith = node_path.join(workspaceRoot, replacement.with);
372
+ try {
373
+ await access(fileReplaceWith, constants.F_OK);
374
+ } catch {
375
+ throw new Error(`The ${fileReplaceWith} path in file replacements does not exist.`);
376
+ }
377
+ fileReplacements ??= {};
378
+ fileReplacements[node_path.join(workspaceRoot, replacement.replace)] = fileReplaceWith;
379
+ }
380
+ const outputHashing = buildOptions.outputHashing;
381
+ const media = 'media';
382
+ const outputNames = {
383
+ bundles: 'all' === outputHashing || 'bundles' === outputHashing ? '[name]-[hash]' : '[name]',
384
+ media: media + ('all' === outputHashing || 'media' === outputHashing ? '/[name]-[hash]' : '/[name]')
385
+ };
386
+ const advancedOptimizations = !!aot && optimizationOptions.scripts;
387
+ const cacheOptions = normalizeCacheOptions(buildOptions.projectMetadata, workspaceRoot);
388
+ const inlineStyleLanguage = buildOptions.inlineStyleLanguage;
389
+ const styles = (buildOptions.styles ?? []).map((stylePath)=>node_path.join(workspaceRoot, stylePath));
390
+ const outputPath = node_path.join(workspaceRoot, buildOptions.outputPath);
391
+ const i18n = normalizeI18nOptions(project.extensions['i18n']);
392
+ const i18nMissingTranslation = buildOptions.i18nMissingTranslation ?? 'warning';
393
+ const normalizedOptions = {
394
+ aot,
395
+ optimizationOptions,
396
+ advancedOptimizations,
397
+ index,
398
+ browser,
399
+ fileReplacements,
400
+ sourcemapOptions,
401
+ tsconfig,
402
+ polyfills: polyfills ?? [],
403
+ outputPath,
404
+ workspaceRoot,
405
+ outputNames,
406
+ cacheOptions,
407
+ inlineStyleLanguage,
408
+ outputHashing,
409
+ styles,
410
+ i18n,
411
+ i18nMissingTranslation
412
+ };
413
+ return normalizedOptions;
414
+ };
415
+ const normalizeI18nOptions = (raw)=>{
416
+ if (!raw || 'object' != typeof raw) return {
417
+ sourceLocale: 'en-US',
418
+ hasDefinedSourceLocale: false
419
+ };
420
+ const i18nConfig = raw;
421
+ let sourceLocale = 'en-US';
422
+ let hasDefinedSourceLocale = false;
423
+ if ('string' == typeof i18nConfig.sourceLocale) {
424
+ sourceLocale = i18nConfig.sourceLocale;
425
+ hasDefinedSourceLocale = true;
426
+ } else if (i18nConfig.sourceLocale && 'object' == typeof i18nConfig.sourceLocale && 'string' == typeof i18nConfig.sourceLocale.code) {
427
+ sourceLocale = i18nConfig.sourceLocale.code;
428
+ hasDefinedSourceLocale = true;
429
+ }
430
+ return {
431
+ sourceLocale,
432
+ hasDefinedSourceLocale
433
+ };
434
+ };
435
+ /**
436
+ * @license
437
+ * Copyright Google LLC All Rights Reserved.
438
+ *
439
+ * Use of this source code is governed by an MIT-style license that can be
440
+ * found in the LICENSE file at https://angular.dev/license
441
+ */ const findUp = (names, from)=>{
442
+ const namesList = Array.isArray(names) ? names : [
443
+ names
444
+ ];
445
+ const root = __rspack_external_node_path_c5b9b54f.parse(from).root;
446
+ let currentDir = from;
447
+ while(currentDir && currentDir !== root){
448
+ for (const name of namesList){
449
+ const p = __rspack_external_node_path_c5b9b54f.join(currentDir, name);
450
+ if (existsSync(p)) return p;
451
+ }
452
+ currentDir = __rspack_external_node_path_c5b9b54f.dirname(currentDir);
453
+ }
454
+ return null;
455
+ };
456
+ const configNames = [
457
+ 'angular.json',
458
+ '.angular.json'
459
+ ];
460
+ const findWorkspaceFile = ()=>findUp(configNames, process.cwd());
461
+ const getAngularWorkspace = async ()=>{
462
+ const host = workspaces.createWorkspaceHost(new NodeJsSyncHost());
463
+ const workspaceFile = findWorkspaceFile();
464
+ if (!workspaceFile) throw new Error("couldn't find workspace file");
465
+ const { workspace } = await workspaces.readWorkspace(workspaceFile, host);
466
+ return {
467
+ basePath: node_path.dirname(workspaceFile),
468
+ workspace
469
+ };
470
+ };
471
+ const findProjectByPath = (workspace, location, basePath)=>{
472
+ const isInside = (base, potential)=>{
473
+ const absoluteBase = node_path.resolve(basePath, base);
474
+ const absolutePotential = node_path.resolve(basePath, potential);
475
+ const relativePotential = node_path.relative(absoluteBase, absolutePotential);
476
+ if (!relativePotential.startsWith('..') && !node_path.isAbsolute(relativePotential)) return true;
477
+ return false;
478
+ };
479
+ const projects = Array.from(workspace.projects).map(([name, project])=>[
480
+ project.root,
481
+ name
482
+ ]).filter((tuple)=>isInside(tuple[0], location)).sort((a, b)=>b[0].length - a[0].length);
483
+ if (0 === projects.length) return null;
484
+ if (projects.length > 1) {
485
+ const found = new Set();
486
+ const sameRoots = projects.filter((v)=>{
487
+ if (!found.has(v[0])) {
488
+ found.add(v[0]);
489
+ return false;
490
+ }
491
+ return true;
492
+ });
493
+ if (sameRoots.length > 0) return null;
494
+ }
495
+ return projects[0][1];
496
+ };
497
+ const getProjectByCwd = (workspace, basePath)=>{
498
+ if (1 === workspace.projects.size) return Array.from(workspace.projects.keys())[0];
499
+ const project = findProjectByPath(workspace, process.cwd(), basePath);
500
+ if (project) return project;
501
+ return null;
502
+ };
503
+ const resolvePages = (workspace, basePath, pages)=>{
504
+ const projectNames = 'all' === pages ? Array.from(workspace.projects.entries()).filter(([, def])=>'application' === def.extensions['projectType']).map(([name])=>name) : pages;
505
+ if (0 === projectNames.length) throw new Error('all' === pages ? 'No application-type projects found in angular.json. Add at least one project with "projectType": "application".' : 'The "pages" option is an empty array. Provide at least one project name.');
506
+ const resolved = [];
507
+ const tsconfigs = new Set();
508
+ for (const name of projectNames){
509
+ const project = workspace.projects.get(name);
510
+ if (!project) throw new Error(`Project "${name}" not found in angular.json. Available projects: ${Array.from(workspace.projects.keys()).join(', ')}`);
511
+ const target = readTarget(project);
512
+ if (!target?.options) throw new Error(`Project "${name}" has no build target or options in angular.json. Make sure it has an architect.build.options section.`);
513
+ const browser = target.options['browser'];
514
+ if (!browser) throw new Error(`Project "${name}" has no "browser" field in its build options. Set it to the entry file path (e.g., "src/main.ts").`);
515
+ const tsConfig = target.options['tsConfig'];
516
+ if (tsConfig) tsconfigs.add(node_path.resolve(basePath, tsConfig));
517
+ resolved.push({
518
+ name,
519
+ browser: node_path.join(basePath, browser)
520
+ });
521
+ }
522
+ if (tsconfigs.size > 1) throw new Error(`Multi-page builds require all projects to share the same tsConfig. Found ${tsconfigs.size} different tsconfigs: ${Array.from(tsconfigs).join(', ')}. Consolidate your projects to use a single tsconfig.`);
523
+ return resolved;
524
+ };
525
+ const HTML_ELEMENT_SUGGESTIONS = {
526
+ div: 'view',
527
+ section: 'view',
528
+ article: 'view',
529
+ aside: 'view',
530
+ header: 'view',
531
+ footer: 'view',
532
+ main: 'view',
533
+ nav: 'view',
534
+ figure: 'view',
535
+ figcaption: 'view',
536
+ details: 'view',
537
+ summary: 'view',
538
+ form: 'view',
539
+ fieldset: 'view',
540
+ button: 'view',
541
+ span: 'text',
542
+ p: 'text',
543
+ h1: 'text',
544
+ h2: 'text',
545
+ h3: 'text',
546
+ h4: 'text',
547
+ h5: 'text',
548
+ h6: 'text',
549
+ label: 'text',
550
+ strong: 'text',
551
+ em: 'text',
552
+ b: 'text',
553
+ i: 'text',
554
+ small: 'text',
555
+ pre: 'text',
556
+ code: 'text',
557
+ blockquote: 'text',
558
+ legend: 'text',
559
+ a: 'text',
560
+ img: 'image',
561
+ ul: 'list',
562
+ ol: 'list',
563
+ li: 'list-item',
564
+ table: 'view',
565
+ thead: 'view',
566
+ tbody: 'view',
567
+ tfoot: 'view',
568
+ tr: 'view',
569
+ td: 'view',
570
+ th: 'view',
571
+ select: null,
572
+ option: null,
573
+ canvas: null,
574
+ video: null,
575
+ audio: null,
576
+ iframe: null,
577
+ br: null,
578
+ hr: null
579
+ };
580
+ const UNSUPPORTED_CSS = {
581
+ float: 'use flex layout',
582
+ clear: 'use flex layout',
583
+ 'text-transform': 'not supported in Lynx',
584
+ outline: 'use border instead',
585
+ 'outline-width': 'use border instead',
586
+ 'outline-style': 'use border instead',
587
+ 'outline-color': 'use border instead',
588
+ 'outline-offset': 'use border instead',
589
+ 'list-style': 'not supported',
590
+ 'list-style-type': 'not supported',
591
+ 'list-style-position': 'not supported',
592
+ 'list-style-image': 'not supported',
593
+ 'table-layout': 'use flex/grid layout',
594
+ 'border-collapse': 'use flex/grid layout',
595
+ 'border-spacing': 'use flex/grid layout',
596
+ 'word-spacing': 'not supported',
597
+ resize: 'not supported',
598
+ content: '::before/::after not supported',
599
+ 'scroll-behavior': 'not supported',
600
+ 'object-fit': 'not supported for images',
601
+ 'object-position': 'not supported for images',
602
+ 'user-select': 'not supported',
603
+ 'will-change': 'not supported',
604
+ 'backdrop-filter': 'use filter instead',
605
+ 'text-justify': 'not supported',
606
+ 'counter-reset': 'CSS counters not supported',
607
+ 'counter-increment': 'CSS counters not supported',
608
+ 'writing-mode': 'use direction property',
609
+ 'text-orientation': 'not supported',
610
+ 'column-count': 'multi-column layout not supported',
611
+ 'column-width': 'multi-column layout not supported',
612
+ 'column-rule': 'multi-column layout not supported'
613
+ };
614
+ const collectElementTags = (code)=>{
615
+ const chainPattern = /ɵɵelement(?:Start)?\([^)]*\)(?:\([^)]*\))*/g;
616
+ const tagExtractor = /\(\d+,\s*["']([a-z][a-z0-9-]*)["']/g;
617
+ const repeaterPattern = /ɵɵrepeaterCreate\(\d+,\s*\w+,\s*\d+,\s*\d+,\s*["']([a-z][a-z0-9-]*)["']/g;
618
+ const tags = new Set();
619
+ let match;
620
+ let tagMatch;
621
+ for (const chain of code.matchAll(chainPattern)){
622
+ tagExtractor.lastIndex = 0;
623
+ while(null !== (tagMatch = tagExtractor.exec(chain[0])))tags.add(tagMatch[1]);
624
+ }
625
+ while(null !== (match = repeaterPattern.exec(code)))tags.add(match[1]);
626
+ return tags;
627
+ };
628
+ const scanCompiledOutputForHtmlElements = (fileCache)=>{
629
+ const diagnostics = [];
630
+ for (const [file, contents] of fileCache){
631
+ if (file.includes('node_modules')) continue;
632
+ const code = 'string' == typeof contents ? contents : Buffer.from(contents).toString();
633
+ const tags = collectElementTags(code);
634
+ const fileName = node_path.relative(process.cwd(), file);
635
+ for (const tag of tags){
636
+ if (!(tag in HTML_ELEMENT_SUGGESTIONS)) continue;
637
+ const replacement = HTML_ELEMENT_SUGGESTIONS[tag];
638
+ const message = null !== replacement ? `${fileName}: <${tag}> is not a Lynx element. Use <${replacement}> instead.` : `${fileName}: <${tag}> has no Lynx equivalent and will not render correctly.`;
639
+ diagnostics.push({
640
+ file,
641
+ message,
642
+ category: 'html-element'
643
+ });
644
+ }
645
+ }
646
+ return diagnostics;
647
+ };
648
+ const scanSourcesForUnsupportedPatterns = (fileNames)=>{
649
+ const diagnostics = [];
650
+ for (const filePath of fileNames){
651
+ if (filePath.includes('node_modules')) continue;
652
+ let source;
653
+ try {
654
+ source = node_fs.readFileSync(filePath, 'utf-8');
655
+ } catch {
656
+ continue;
657
+ }
658
+ if (source.includes('ViewEncapsulation.ShadowDom')) {
659
+ const fileName = node_path.relative(process.cwd(), filePath);
660
+ diagnostics.push({
661
+ file: filePath,
662
+ message: `${fileName}: ViewEncapsulation.ShadowDom is not supported in Lynx (falls back to None). Use Emulated instead.`,
663
+ category: 'structural'
664
+ });
665
+ }
666
+ }
667
+ return diagnostics;
668
+ };
669
+ const scanCompiledOutputForStructuralIssues = (fileCache)=>{
670
+ const diagnostics = [];
671
+ for (const [file, contents] of fileCache){
672
+ if (file.includes('node_modules')) continue;
673
+ const code = 'string' == typeof contents ? contents : Buffer.from(contents).toString();
674
+ const tags = collectElementTags(code);
675
+ const fileName = node_path.relative(process.cwd(), file);
676
+ if (tags.has('list-item') && !tags.has('list')) diagnostics.push({
677
+ file,
678
+ message: `${fileName}: <list-item> used without a <list> parent. Wrap list items in a <list> element.`,
679
+ category: 'structural'
680
+ });
681
+ }
682
+ return diagnostics;
683
+ };
684
+ const scanSourcesForUnsupportedCss = (fileNames)=>{
685
+ const diagnostics = [];
686
+ for (const filePath of fileNames){
687
+ if (filePath.includes('node_modules')) continue;
688
+ let source;
689
+ try {
690
+ source = node_fs.readFileSync(filePath, 'utf-8');
691
+ } catch {
692
+ continue;
693
+ }
694
+ if (!source.includes('@Component')) continue;
695
+ const cssChunks = [];
696
+ const stylesBlockMatch = source.match(/styles\s*:\s*\[([\s\S]*?)\]/);
697
+ if (stylesBlockMatch) {
698
+ for (const m of stylesBlockMatch[1].matchAll(/`([\s\S]*?)`/g))cssChunks.push(m[1]);
699
+ if (0 === cssChunks.length) for (const m of stylesBlockMatch[1].matchAll(/(['"])([\s\S]*?)\1/g))cssChunks.push(m[2]);
700
+ }
701
+ const styleUrlsMatch = source.match(/styleUrls?\s*:\s*\[([\s\S]*?)\]/);
702
+ if (styleUrlsMatch) for (const m of styleUrlsMatch[1].matchAll(/['"]([^'"]+)['"]/g)){
703
+ const stylePath = node_path.resolve(node_path.dirname(filePath), m[1]);
704
+ try {
705
+ cssChunks.push(node_fs.readFileSync(stylePath, 'utf-8'));
706
+ } catch {}
707
+ }
708
+ const seen = new Set();
709
+ const fileName = node_path.relative(process.cwd(), filePath);
710
+ for (const css of cssChunks){
711
+ const withoutComments = css.replace(/\/\*[\s\S]*?\*\//g, '').replace(/\/\/.*/g, '');
712
+ const propertyPattern = /(?:^|[{;])\s*([a-z-]+)\s*:/gm;
713
+ let match;
714
+ while(null !== (match = propertyPattern.exec(withoutComments))){
715
+ const property = match[1];
716
+ if (UNSUPPORTED_CSS[property] && !seen.has(property)) {
717
+ seen.add(property);
718
+ diagnostics.push({
719
+ file: filePath,
720
+ message: `${fileName}: '${property}' — ${UNSUPPORTED_CSS[property]}.`,
721
+ category: 'css'
722
+ });
723
+ }
724
+ }
725
+ }
726
+ }
727
+ return diagnostics;
728
+ };
729
+ const reportLynxDiagnostics = (diagnostics)=>{
730
+ if (0 === diagnostics.length) return;
731
+ const htmlWarnings = diagnostics.filter((d)=>'html-element' === d.category);
732
+ const structuralWarnings = diagnostics.filter((d)=>'structural' === d.category);
733
+ const cssWarnings = diagnostics.filter((d)=>'css' === d.category);
734
+ if (htmlWarnings.length > 0) {
735
+ console.warn('[Lynx] HTML elements detected in templates:');
736
+ for (const w of htmlWarnings)console.warn(` ${w.message}`);
737
+ }
738
+ if (structuralWarnings.length > 0) {
739
+ console.warn('[Lynx] Structural issues:');
740
+ for (const w of structuralWarnings)console.warn(` ${w.message}`);
741
+ }
742
+ if (cssWarnings.length > 0) {
743
+ console.warn('[Lynx] Unsupported CSS properties:');
744
+ for (const w of cssWarnings)console.warn(` ${w.message}`);
745
+ }
746
+ };
747
+ const applyAngularRules = async (api, pluginOptions)=>{
748
+ const { basePath, workspace } = await getAngularWorkspace();
749
+ if (pluginOptions.pages) {
750
+ const pages = resolvePages(workspace, basePath, pluginOptions.pages);
751
+ api.modifyRsbuildConfig((config)=>{
752
+ config.source ??= {};
753
+ config.source.entry = Object.fromEntries(pages.map((page)=>[
754
+ page.name,
755
+ page.browser
756
+ ]));
757
+ });
758
+ }
759
+ const primaryProjectName = pluginOptions.pages ? (Array.isArray(pluginOptions.pages) ? pluginOptions.pages[0] : Array.from(workspace.projects.entries()).find(([, def])=>'application' === def.extensions['projectType'])?.[0]) ?? null : getProjectByCwd(workspace, basePath);
760
+ if (!primaryProjectName) throw new Error("couldn't find the project");
761
+ const projectDefinition = workspace.projects.get(primaryProjectName);
762
+ if (!projectDefinition) throw new Error(`Project "${primaryProjectName}" not found in workspace`);
763
+ const buildOptions = await readBuildOptions(projectDefinition, basePath);
764
+ applyAngularConfig(api, buildOptions);
765
+ const sourcemap = !!(!!buildOptions.sourcemapOptions.scripts && (buildOptions.sourcemapOptions.hidden ? 'external' : true));
766
+ const thirdPartySourcemaps = buildOptions.sourcemapOptions.vendor;
767
+ const advancedOptimizations = buildOptions.advancedOptimizations;
768
+ const aot = buildOptions.aot;
769
+ const javascriptTransformer = new JavaScriptTransformer({
770
+ sourcemap,
771
+ thirdPartySourcemaps,
772
+ advancedOptimizations,
773
+ jit: !aot,
774
+ maxConcurrency: maxWorkers
775
+ }, void 0);
776
+ const tsconfig = buildOptions.tsconfig;
777
+ const compilation = await createAngularCompilation(false, aot);
778
+ const typeScriptFileCache = new Map();
779
+ const scopedCssCacheDir = node_path.join(basePath, 'node_modules', '.cache', 'angular-lynx-css');
780
+ node_fs.mkdirSync(scopedCssCacheDir, {
781
+ recursive: true
782
+ });
783
+ const componentStylesCache = new Map();
784
+ const componentScopeIds = new Map();
785
+ let componentResourceDependencies = new Map();
786
+ const writtenStylesheets = new Set();
787
+ let knownSourceFiles = new Set();
788
+ const transformStylesheet = createTransformStylesheet({
789
+ basePath,
790
+ scopedCssCacheDir,
791
+ componentStylesCache,
792
+ componentScopeIds,
793
+ onStylesheetWritten: (filePath)=>writtenStylesheets.add(filePath)
794
+ });
795
+ const compileAngular = async (changedFiles)=>{
796
+ const { sourceFileCache, fileNames } = buildLynxSchemaSourceFileCache(tsconfig, changedFiles && ((file)=>changedFiles.has(node_path.normalize(file)) || !knownSourceFiles.has(file)));
797
+ knownSourceFiles = new Set(fileNames);
798
+ if (changedFiles) await compilation.update?.(new Set(changedFiles));
799
+ const seeded = seedCompilationSourceFiles(compilation, sourceFileCache);
800
+ const seededFiles = new Set(seeded ? [
801
+ ...sourceFileCache.keys()
802
+ ].map((file)=>node_path.normalize(file)) : []);
803
+ writtenStylesheets.clear();
804
+ let result;
805
+ try {
806
+ result = await compilation.initialize(tsconfig, {
807
+ processWebWorker: (workerFile, _containingFile)=>workerFile,
808
+ transformStylesheet,
809
+ modifiedFiles: changedFiles && new Set([
810
+ ...changedFiles
811
+ ].filter((file)=>!seededFiles.has(file)))
812
+ }, {
813
+ sourcemap: !!sourcemap,
814
+ preserveSymlinks: false
815
+ });
816
+ } catch (error) {
817
+ throw new Error(`Angular compilation failed to initialize for "${tsconfig}". This often means a compiler worker crashed under resource pressure — lower NG_BUILD_MAX_WORKERS or reduce parallel build concurrency. Original error: ${error instanceof Error ? error.stack ?? error.message : String(error)}`);
818
+ }
819
+ componentResourceDependencies = new Map([
820
+ ...result.componentResourcesDependencies ?? []
821
+ ].map(([file, dependencies])=>[
822
+ node_path.normalize(file),
823
+ dependencies.map((dependency)=>node_path.normalize(dependency))
824
+ ]));
825
+ const changedOutputs = new Set();
826
+ try {
827
+ for (const { filename, contents } of (await compilation.emitAffectedFiles())){
828
+ const file = node_path.normalize(filename);
829
+ if (typeScriptFileCache.get(file) !== contents) changedOutputs.add(file);
830
+ typeScriptFileCache.set(file, contents);
831
+ }
832
+ } catch (error) {
833
+ throw new Error(`Angular failed to emit compiled output for "${tsconfig}". Original error: ${error instanceof Error ? error.stack ?? error.message : String(error)}`);
834
+ }
835
+ for (const file of writtenStylesheets)changedOutputs.add(file);
836
+ for (const file of changedFiles ?? [])if (!node_fs.existsSync(file)) typeScriptFileCache.delete(file);
837
+ reportLynxDiagnostics([
838
+ ...scanCompiledOutputForHtmlElements(typeScriptFileCache),
839
+ ...scanCompiledOutputForStructuralIssues(typeScriptFileCache),
840
+ ...scanSourcesForUnsupportedPatterns(fileNames),
841
+ ...scanSourcesForUnsupportedCss(fileNames)
842
+ ]);
843
+ const diagnostics = await compilation.diagnoseFiles(useTypeChecking ? DiagnosticModes.All : DiagnosticModes.All & ~DiagnosticModes.Semantic);
844
+ const actionableErrors = diagnostics.errors?.filter((e)=>!isLynxUnknownElementMessage(e.text));
845
+ const actionableWarnings = diagnostics.warnings?.filter((w)=>!isLynxUnknownElementMessage(w.text));
846
+ if (actionableErrors?.length || actionableWarnings?.length) console.log({
847
+ errors: actionableErrors,
848
+ warnings: actionableWarnings
849
+ });
850
+ return {
851
+ changedOutputs,
852
+ inputFiles: result.referencedFiles.map((file)=>node_path.normalize(file))
853
+ };
854
+ };
855
+ const coordinator = createAngularRebuildCoordinator({
856
+ compile: compileAngular
857
+ });
858
+ const compilersByEnvironment = new Map();
859
+ api.onAfterCreateCompiler(({ compiler })=>{
860
+ const compilers = 'compilers' in compiler ? compiler.compilers : [
861
+ compiler
862
+ ];
863
+ for (const environmentCompiler of compilers)if (environmentCompiler.name) compilersByEnvironment.set(environmentCompiler.name, environmentCompiler);
864
+ });
865
+ let compilationClosed = false;
866
+ api.onBeforeEnvironmentCompile(async ({ environment, isWatch })=>{
867
+ const compiler = compilersByEnvironment.get(environment.name);
868
+ const outdatedFiles = await coordinator.prepare(environment.name, [
869
+ ...compiler?.modifiedFiles ?? [],
870
+ ...compiler?.removedFiles ?? []
871
+ ]);
872
+ if (compiler && outdatedFiles.size > 0) compiler.modifiedFiles = new Set([
873
+ ...compiler.modifiedFiles ?? [],
874
+ ...outdatedFiles
875
+ ]);
876
+ if (!isWatch && !compilationClosed) {
877
+ compilationClosed = true;
878
+ await compilation.close?.();
879
+ }
880
+ });
881
+ api.transform({
882
+ test: /\.[cm]?[jt]sx?$/
883
+ }, async (context)=>{
884
+ const isJs = /\.[cm]?js$/.test(context.resourcePath);
885
+ if (isJs) {
886
+ const contents = await javascriptTransformer.transformData(context.resourcePath, context.code, {
887
+ skipLinker: false,
888
+ sideEffects: ()=>Promise.resolve(false)
889
+ });
890
+ return {
891
+ code: transformWorklets(Buffer.from(contents).toString(), context.resourcePath)
892
+ };
893
+ }
894
+ const content = typeScriptFileCache.get(context.resourcePath);
895
+ if (!content) throw new Error(`No compiled output found for ${context.resourcePath}`);
896
+ for (const dependency of componentResourceDependencies.get(context.resourcePath) ?? [])context.addDependency(dependency);
897
+ const code = 'string' == typeof content ? content : Buffer.from(content).toString();
898
+ return {
899
+ code: buildTransformedCode({
900
+ code,
901
+ resourcePath: context.resourcePath,
902
+ componentStyles: componentStylesCache.get(context.resourcePath),
903
+ scopeInfo: componentScopeIds.get(context.resourcePath)
904
+ })
905
+ };
906
+ });
907
+ };
908
+ const LAYERS = {
909
+ BACKGROUND: 'background',
910
+ MAIN_THREAD: 'main'
911
+ };
912
+ const applyLayers = (api)=>{
913
+ api.modifyBundlerChain((chain)=>{
914
+ chain.module.rule("typescript").oneOf(LAYERS.BACKGROUND).layer(LAYERS.BACKGROUND).test(/\.[cm]?[jt]sx?$/).use('builtin:swc-loader').loader('builtin:swc-loader').options({
915
+ jsc: {
916
+ target: 'es2015',
917
+ parser: {
918
+ syntax: "typescript"
919
+ }
920
+ }
921
+ }).end();
922
+ chain.module.rule("typescript").oneOf(LAYERS.MAIN_THREAD).layer(LAYERS.MAIN_THREAD).test(/\.[cm]?[jt]sx?$/).use('builtin:swc-loader').loader('builtin:swc-loader').options({
923
+ jsc: {
924
+ target: 'es2019',
925
+ parser: {
926
+ syntax: "typescript"
927
+ }
928
+ }
929
+ }).end();
930
+ });
931
+ };
932
+ const applyCSS = (api, options)=>{
933
+ const { enableRemoveCSSScope, enableCSSSelector, enableCSSInvalidation, targetSdkVersion } = options;
934
+ api.modifyRsbuildConfig((config, { mergeRsbuildConfig })=>mergeRsbuildConfig(config, {
935
+ output: {
936
+ injectStyles: false
937
+ }
938
+ }));
939
+ const __dirname = node_path.dirname(fileURLToPath(import.meta.url));
940
+ api.modifyBundlerChain(async (chain, { CHAIN_ID, environment })=>{
941
+ const { CssExtractRspackPlugin } = await import("@lynx-js/css-extract-webpack-plugin");
942
+ const removeLightningCSS = (rule)=>{
943
+ if (rule.uses.has(CHAIN_ID.USE.LIGHTNINGCSS) && 'lynx' === environment.name) rule.uses.delete(CHAIN_ID.USE.LIGHTNINGCSS);
944
+ };
945
+ const cssRules = [
946
+ CHAIN_ID.RULE.CSS,
947
+ CHAIN_ID.RULE.SASS,
948
+ CHAIN_ID.RULE.LESS,
949
+ CHAIN_ID.RULE.STYLUS
950
+ ];
951
+ for (const ruleName of cssRules.filter((rule)=>chain.module.rules.has(rule))){
952
+ const rule = chain.module.rule(ruleName);
953
+ const mainRuleName = ruleName === CHAIN_ID.RULE.CSS ? CHAIN_ID.ONE_OF.CSS_MAIN : ruleName;
954
+ const mainRule = rule.oneOf(mainRuleName);
955
+ const parentRuleEntries = rule.entries();
956
+ removeLightningCSS(mainRule);
957
+ mainRule.issuerLayer(LAYERS.BACKGROUND).use(CHAIN_ID.USE.MINI_CSS_EXTRACT).loader(CssExtractRspackPlugin.loader).end();
958
+ const uses = mainRule.uses.entries() ?? {};
959
+ const ruleEntries = mainRule.entries();
960
+ const cssLoader = uses[CHAIN_ID.USE.CSS];
961
+ if (!cssLoader) continue;
962
+ const cssLoaderRule = cssLoader.entries();
963
+ const mainThreadLayerRule = chain.module.rule(`${ruleName}:${LAYERS.MAIN_THREAD}`).test(parentRuleEntries.test).merge(ruleEntries).issuerLayer(LAYERS.MAIN_THREAD);
964
+ if (void 0 !== parentRuleEntries.dependency) mainThreadLayerRule.merge({
965
+ dependency: parentRuleEntries.dependency
966
+ });
967
+ mainThreadLayerRule.use(CHAIN_ID.USE.IGNORE_CSS).loader(node_path.resolve(__dirname, './loaders/ignore-css-loader')).end().uses.merge(uses).delete(CHAIN_ID.USE.MINI_CSS_EXTRACT).delete(CHAIN_ID.USE.LIGHTNINGCSS).delete(CHAIN_ID.USE.CSS).end().use(CHAIN_ID.USE.CSS).after(CHAIN_ID.USE.IGNORE_CSS).merge(cssLoaderRule).options(normalizeCssLoaderOptions(cssLoaderRule.options, true)).end();
968
+ }
969
+ for (const ruleName of cssRules.filter((rule)=>chain.module.rules.has(rule))){
970
+ const inlineRuleName = ruleName === CHAIN_ID.RULE.CSS ? CHAIN_ID.ONE_OF.CSS_INLINE : `${ruleName}-inline`;
971
+ removeLightningCSS(chain.module.rule(ruleName).oneOf(inlineRuleName));
972
+ }
973
+ if ('lynx' === environment.name && chain.module.rules.has(CHAIN_ID.RULE.FONT)) {
974
+ const fontRule = chain.module.rule(CHAIN_ID.RULE.FONT);
975
+ fontRule.oneOfs.clear();
976
+ fontRule.type('asset/inline');
977
+ }
978
+ chain.plugin(CHAIN_ID.PLUGIN.MINI_CSS_EXTRACT).tap(([options])=>[
979
+ {
980
+ ...options,
981
+ enableRemoveCSSScope,
982
+ enableCSSSelector,
983
+ enableCSSInvalidation,
984
+ targetSdkVersion,
985
+ cssPlugins: [
986
+ CSSPlugins.parserPlugins.removeFunctionWhiteSpace()
987
+ ]
988
+ }
989
+ ]).init((_, args)=>new CssExtractRspackPlugin(...args)).end().end();
990
+ chain.module.when(void 0 === enableRemoveCSSScope, (module)=>module.rule('lynx.css.scoped').test(/\.css$/).resourceQuery({
991
+ and: [
992
+ /cssId/,
993
+ {
994
+ not: /common/
995
+ }
996
+ ]
997
+ }).sideEffects(false));
998
+ });
999
+ };
1000
+ const normalizeCssLoaderOptions = (options, exportOnlyLocals)=>{
1001
+ if (options.modules && exportOnlyLocals) {
1002
+ let { modules } = options;
1003
+ modules = true === modules ? {
1004
+ exportOnlyLocals: true
1005
+ } : 'string' == typeof modules ? {
1006
+ mode: modules,
1007
+ exportOnlyLocals: true
1008
+ } : {
1009
+ ...modules,
1010
+ exportOnlyLocals: true
1011
+ };
1012
+ return {
1013
+ ...options,
1014
+ modules
1015
+ };
1016
+ }
1017
+ return options;
1018
+ };
1019
+ const createLynxProcessEvalResultRuntimeModule = (webpack)=>class extends webpack.RuntimeModule {
1020
+ constructor(){
1021
+ super('Lynx process eval result', webpack.RuntimeModule.STAGE_ATTACH);
1022
+ }
1023
+ generate() {
1024
+ const chunk = this.chunk;
1025
+ const compilation = this.compilation;
1026
+ if (!chunk || !compilation) return '';
1027
+ const chunkGroup = chunk._groupsIterable?.[0];
1028
+ const modules = compilation.chunkGraph?.getChunkModules(chunk) ?? [];
1029
+ modules.sort((a, b)=>{
1030
+ const aIdx = chunkGroup?.getModulePreOrderIndex(a) ?? 0;
1031
+ const bIdx = chunkGroup?.getModulePreOrderIndex(b) ?? 0;
1032
+ return aIdx - bIdx;
1033
+ });
1034
+ const sortedIds = modules.map((m)=>compilation.chunkGraph.getModuleId(m)).filter((id)=>null != id);
1035
+ const moduleOrder = JSON.stringify(sortedIds);
1036
+ return `
1037
+ ${RuntimeGlobals.lynxProcessEvalResult} = function (result, schema) {
1038
+ var chunk = result && result(schema);
1039
+ if (chunk && chunk.ids && chunk.modules) {
1040
+ ${webpack.RuntimeGlobals.externalInstallChunk}(chunk);
1041
+ var moduleOrder = ${moduleOrder};
1042
+ for (var i = 0; i < moduleOrder.length; i++) {
1043
+ if (chunk.modules[moduleOrder[i]]) {
1044
+ ${webpack.RuntimeGlobals.require}(moduleOrder[i]);
1045
+ }
1046
+ }
1047
+ return chunk;
1048
+ }
1049
+ return chunk
1050
+ }
1051
+ `;
1052
+ }
1053
+ };
1054
+ class AngularWebpackPlugin {
1055
+ options;
1056
+ constructor(options){
1057
+ this.options = options;
1058
+ }
1059
+ static defaultOptions = Object.freeze({
1060
+ disableCreateSelectorQueryIncompatibleWarning: false,
1061
+ firstScreenSyncTiming: 'immediately',
1062
+ enableSSR: false,
1063
+ isWeb: false,
1064
+ mainThreadChunks: [],
1065
+ extractStr: false,
1066
+ experimental_isLazyBundle: false
1067
+ });
1068
+ apply(compiler) {
1069
+ const options = Object.assign({}, AngularWebpackPlugin.defaultOptions, this.options);
1070
+ if (options.enableSSR && 'jsReady' !== options.firstScreenSyncTiming) options.firstScreenSyncTiming = 'jsReady';
1071
+ const { DefinePlugin, EnvironmentPlugin } = compiler.webpack;
1072
+ new EnvironmentPlugin({
1073
+ DEBUG: null
1074
+ }).apply(compiler);
1075
+ new DefinePlugin({
1076
+ __DEV__: JSON.stringify('development' === compiler.options.mode),
1077
+ __PROFILE__: JSON.stringify('development' === compiler.options.mode),
1078
+ __EXTRACT_STR__: JSON.stringify(Boolean(options.extractStr)),
1079
+ __FIRST_SCREEN_SYNC_TIMING__: JSON.stringify(options.firstScreenSyncTiming),
1080
+ __ENABLE_SSR__: JSON.stringify(options.enableSSR),
1081
+ __WEB__: JSON.stringify(Boolean(options.isWeb)),
1082
+ __DISABLE_CREATE_SELECTOR_QUERY_INCOMPATIBLE_WARNING__: JSON.stringify(options.disableCreateSelectorQueryIncompatibleWarning)
1083
+ }).apply(compiler);
1084
+ compiler.hooks.thisCompilation.tap(this.constructor.name, (compilation)=>{
1085
+ const onceForChunkSet = new WeakSet();
1086
+ compilation.hooks.runtimeRequirementInTree.for(compiler.webpack.RuntimeGlobals.ensureChunkHandlers).tap('VanillaWebpackPlugin', (chunk, runtimeRequirements)=>{
1087
+ runtimeRequirements.add(RuntimeGlobals.lynxProcessEvalResult);
1088
+ });
1089
+ compilation.hooks.runtimeRequirementInTree.for(RuntimeGlobals.lynxProcessEvalResult).tap('VanillaWebpackPlugin', (chunk)=>{
1090
+ if (onceForChunkSet.has(chunk)) return;
1091
+ onceForChunkSet.add(chunk);
1092
+ const LynxProcessEvalResultRuntimeModule = createLynxProcessEvalResultRuntimeModule(compiler.webpack);
1093
+ compilation.addRuntimeModule(chunk, new LynxProcessEvalResultRuntimeModule());
1094
+ });
1095
+ compilation.hooks.processAssets.tap({
1096
+ name: this.constructor.name,
1097
+ stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL
1098
+ }, ()=>{
1099
+ for (const name of options.mainThreadChunks ?? [])this.#updateMainThreadInfo(compilation, name);
1100
+ for (const cg of compilation.chunkGroups.filter((cg)=>!cg.isInitial()).filter((cg)=>cg.origins.every((origin)=>origin.module?.layer === LAYERS.MAIN_THREAD))){
1101
+ const files = cg.getFiles();
1102
+ for (const name of files.filter((name)=>name.endsWith('.js')))this.#updateMainThreadInfo(compilation, name);
1103
+ }
1104
+ });
1105
+ {
1106
+ const { ConcatSource } = compiler.webpack.sources;
1107
+ const mainThreadChunkSet = new Set(options.mainThreadChunks ?? []);
1108
+ compilation.hooks.processAssets.tap({
1109
+ name: `${this.constructor.name}:threadGlobals`,
1110
+ stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONS
1111
+ }, ()=>{
1112
+ for (const [name] of Object.entries(compilation.assets).filter(([n])=>n.endsWith('.js'))){
1113
+ const isMainThread = mainThreadChunkSet.has(name);
1114
+ const prefix = `globalThis["__MAIN_THREAD__"]=${isMainThread};`;
1115
+ compilation.updateAsset(name, (old)=>new ConcatSource(prefix, old));
1116
+ }
1117
+ if (!options.experimental_isLazyBundle) for (const name of options.mainThreadChunks ?? []){
1118
+ const asset = compilation.getAsset(name);
1119
+ if (!asset) continue;
1120
+ const source = asset.source.source().toString();
1121
+ const useStrictPrefix = /^(['"])use strict\1;?/.test(source) ? "'use strict';" : '';
1122
+ compilation.updateAsset(name, (old)=>new ConcatSource(`${useStrictPrefix}var globDynamicComponentEntry=globDynamicComponentEntry||'__Card__';`, old));
1123
+ }
1124
+ });
1125
+ }
1126
+ const hooks = LynxTemplatePlugin.getLynxTemplatePluginHooks(compilation);
1127
+ const { ConcatSource } = compiler.webpack.sources;
1128
+ hooks.beforeEncode.tap(this.constructor.name, (args)=>{
1129
+ const { encodeData } = args;
1130
+ encodeData.sourceContent.config = {
1131
+ ...encodeData.sourceContent.config,
1132
+ enableCSSInlineVariables: true
1133
+ };
1134
+ if (!encodeData.lepusCode.root) return args;
1135
+ if ('card' === encodeData.sourceContent.appType) return args;
1136
+ compilation.updateAsset(encodeData.lepusCode.root.name, (old)=>new ConcatSource(`\
1137
+ (function (globDynamicComponentEntry) {
1138
+ const module = { exports: {} }
1139
+ const exports = module.exports
1140
+ `, old, `
1141
+ ;return module.exports
1142
+ })`));
1143
+ return args;
1144
+ });
1145
+ });
1146
+ }
1147
+ #updateMainThreadInfo(compilation, name) {
1148
+ const asset = compilation.getAsset(name);
1149
+ tiny_invariant(asset, `Should have main thread asset ${name}`);
1150
+ compilation.updateAsset(asset.name, asset.source, {
1151
+ ...asset.info,
1152
+ 'lynx:main-thread': true
1153
+ });
1154
+ }
1155
+ }
1156
+ const S_LYNX_CONFIG = Symbol.for('@lynx-js/rsbuild-plugin:config');
1157
+ const PLUGIN_NAME_TEMPLATE = 'lynx:template';
1158
+ const PLUGIN_NAME_RUNTIME_WRAPPER = 'lynx:runtime-wrapper';
1159
+ const PLUGIN_NAME_ANGULAR = 'lynx:angular';
1160
+ const applyEntry = (api, options)=>{
1161
+ const { customCSSInheritanceList, debugInfoOutside, defaultDisplayLinear, enableAccessibilityElement, enableCSSInheritance, enableCSSInvalidation, enableCSSSelector, enableNewGesture, enableRemoveCSSScope, removeDescendantSelectorScope, targetSdkVersion } = options;
1162
+ api.modifyBundlerChain((chain, { environment, isDev })=>{
1163
+ const lynxConfig = api.useExposed(S_LYNX_CONFIG);
1164
+ if (!lynxConfig) throw new Error('No Lynx config exposed. `pluginLynx` has to be applied for the Lynx build engine to be configured.');
1165
+ const isLynx = 'lynx' === environment.name;
1166
+ const isWeb = 'web' === environment.name;
1167
+ const { hmr, liveReload } = environment.config.dev ?? {};
1168
+ const enabledHMR = isDev && !isWeb && false !== hmr;
1169
+ const enabledLiveReload = isDev && !isWeb && false !== liveReload;
1170
+ const entries = chain.entryPoints.entries() ?? {};
1171
+ chain.entryPoints.clear();
1172
+ const mainThreadChunks = [];
1173
+ for (const [entryName, entryPoint] of Object.entries(entries)){
1174
+ const { imports } = getChunks(entryName, entryPoint.values());
1175
+ const templateFilename = lynxConfig.resolveBundleFilename({
1176
+ entryName,
1177
+ platform: environment.name
1178
+ });
1179
+ const mainThreadEntry = `${entryName}__main-thread`;
1180
+ const mainThreadName = node_path.posix.join(`${entryName}/main-thread.js`);
1181
+ const backgroundEntry = entryName;
1182
+ const backgroundThreadName = node_path.posix.join(`${entryName}/background-thread.js`);
1183
+ mainThreadChunks.push(mainThreadName);
1184
+ chain.entry(mainThreadEntry).add({
1185
+ layer: LAYERS.MAIN_THREAD,
1186
+ import: imports,
1187
+ filename: mainThreadName
1188
+ }).end();
1189
+ chain.entry(backgroundEntry).add({
1190
+ layer: LAYERS.BACKGROUND,
1191
+ import: imports,
1192
+ filename: backgroundThreadName
1193
+ }).when(enabledHMR, (entry)=>{
1194
+ entry.prepend({
1195
+ layer: LAYERS.BACKGROUND,
1196
+ import: '@rspack/core/hot/dev-server'
1197
+ });
1198
+ }).when(enabledHMR || enabledLiveReload, (entry)=>{
1199
+ entry.prepend({
1200
+ layer: LAYERS.BACKGROUND,
1201
+ import: '@lynx-js/webpack-dev-transport/client'
1202
+ });
1203
+ }).end();
1204
+ chain.plugin(`${PLUGIN_NAME_TEMPLATE}-${entryName}`).use(LynxTemplatePlugin, [
1205
+ {
1206
+ filename: templateFilename,
1207
+ intermediate: lynxConfig.resolveIntermediateDir({
1208
+ entryName
1209
+ }),
1210
+ chunks: [
1211
+ mainThreadEntry,
1212
+ backgroundEntry
1213
+ ],
1214
+ cssPlugins: [
1215
+ CSSPlugins.parserPlugins.removeFunctionWhiteSpace()
1216
+ ],
1217
+ customCSSInheritanceList,
1218
+ debugInfoOutside,
1219
+ defaultDisplayLinear,
1220
+ enableAccessibilityElement,
1221
+ enableCSSInheritance,
1222
+ enableCSSInvalidation,
1223
+ enableCSSSelector,
1224
+ enableNewGesture,
1225
+ enableRemoveCSSScope,
1226
+ removeDescendantSelectorScope,
1227
+ targetSdkVersion,
1228
+ enableA11y: true
1229
+ }
1230
+ ]).end();
1231
+ }
1232
+ if (isLynx) {
1233
+ chain.plugin(PLUGIN_NAME_RUNTIME_WRAPPER).use(RuntimeWrapperWebpackPlugin, [
1234
+ {
1235
+ injectVars (vars) {
1236
+ return vars.map((name)=>{
1237
+ if ('Component' === name) return '__Component';
1238
+ return name;
1239
+ });
1240
+ },
1241
+ targetSdkVersion,
1242
+ test: /^(?!.*main-thread(?:\.[A-Fa-f0-9]*)?\.js$).*\.js$/
1243
+ }
1244
+ ]).end();
1245
+ chain.plugin(`${LynxEncodePlugin.name}`).use(LynxEncodePlugin, [
1246
+ {}
1247
+ ]).end();
1248
+ }
1249
+ if (isWeb) chain.plugin(`${WebEncodePlugin.name}`).use(WebEncodePlugin, []).end();
1250
+ chain.plugin(PLUGIN_NAME_ANGULAR).after(PLUGIN_NAME_TEMPLATE).use(AngularWebpackPlugin, [
1251
+ {
1252
+ mainThreadChunks,
1253
+ enableSSR: options.enableSSR,
1254
+ isWeb
1255
+ }
1256
+ ]);
1257
+ });
1258
+ };
1259
+ const getChunks = (entryName, entryValue)=>{
1260
+ const chunks = [
1261
+ entryName
1262
+ ];
1263
+ const imports = [];
1264
+ for (const item of entryValue){
1265
+ if ('string' == typeof item) {
1266
+ imports.push(item);
1267
+ continue;
1268
+ }
1269
+ if (Array.isArray(item)) {
1270
+ imports.push(...imports);
1271
+ continue;
1272
+ }
1273
+ const { dependOn } = item;
1274
+ if (Array.isArray(item.import)) imports.push(...item.import);
1275
+ else imports.push(item.import);
1276
+ if (dependOn) if ('string' == typeof dependOn) chunks.unshift(dependOn);
1277
+ else chunks.unshift(...dependOn);
1278
+ }
1279
+ return {
1280
+ chunks,
1281
+ imports
1282
+ };
1283
+ };
1284
+ const applyGenerator = (api)=>{
1285
+ api.modifyBundlerChain({
1286
+ order: 'pre',
1287
+ handler: (chain)=>{
1288
+ chain.module.rule(`json-parse:${LAYERS.MAIN_THREAD}`).issuerLayer(LAYERS.MAIN_THREAD).test(/\.json$/).type('json').generator({
1289
+ JSONParse: false
1290
+ });
1291
+ }
1292
+ });
1293
+ };
1294
+ const RESET = '\x1b[0m';
1295
+ const GRAY = '\x1b[90m';
1296
+ const RED = '\x1b[31m';
1297
+ const YELLOW = '\x1b[33m';
1298
+ const CYAN = '\x1b[36m';
1299
+ const WHITE = '\x1b[37m';
1300
+ const BOLD = '\x1b[1m';
1301
+ const LEVEL_COLORS = {
1302
+ log: WHITE,
1303
+ warn: `${BOLD}${YELLOW}`,
1304
+ error: `${BOLD}${RED}`,
1305
+ info: CYAN,
1306
+ debug: GRAY
1307
+ };
1308
+ const LEVEL_LABELS = {
1309
+ log: 'LOG',
1310
+ warn: 'WRN',
1311
+ error: 'ERR',
1312
+ info: 'INF',
1313
+ debug: 'DBG'
1314
+ };
1315
+ const formatTimestamp = (ts)=>{
1316
+ const d = new Date(ts);
1317
+ const h = String(d.getHours()).padStart(2, '0');
1318
+ const m = String(d.getMinutes()).padStart(2, '0');
1319
+ const s = String(d.getSeconds()).padStart(2, '0');
1320
+ const ms = String(d.getMilliseconds()).padStart(3, '0');
1321
+ return `${h}:${m}:${s}.${ms}`;
1322
+ };
1323
+ const formatArg = (arg)=>{
1324
+ if (null == arg) return String(arg);
1325
+ if ('string' == typeof arg) return arg;
1326
+ if ('object' == typeof arg && 'Error' === arg.__type) {
1327
+ const err = arg;
1328
+ return err.stack || `${err.name}: ${err.message}`;
1329
+ }
1330
+ try {
1331
+ return JSON.stringify(arg, null, 2);
1332
+ } catch {
1333
+ return String(arg);
1334
+ }
1335
+ };
1336
+ let logFile = null;
1337
+ let logFilePath = '';
1338
+ const getLogFilePath = ()=>logFilePath;
1339
+ const ensureLogFile = ()=>{
1340
+ if (!logFile) {
1341
+ logFilePath = node_path.resolve(process.cwd(), '.dev-logs');
1342
+ logFile = node_fs.createWriteStream(logFilePath, {
1343
+ flags: 'w'
1344
+ });
1345
+ }
1346
+ return logFile;
1347
+ };
1348
+ const writeLog = (entry)=>{
1349
+ const stream = ensureLogFile();
1350
+ const color = LEVEL_COLORS[entry.level] || WHITE;
1351
+ const label = LEVEL_LABELS[entry.level] || entry.level.toUpperCase();
1352
+ const time = formatTimestamp(entry.timestamp);
1353
+ const args = entry.args.map(formatArg).join(' ');
1354
+ stream.write(`${GRAY}${time}${RESET} ${color}${label}${RESET} ${args}\n`);
1355
+ };
1356
+ const createDevLoggerMiddleware = ()=>(req, res, next)=>{
1357
+ if ('OPTIONS' === req.method && '/__dev_logs' === req.url) {
1358
+ res.writeHead(204, {
1359
+ 'Access-Control-Allow-Origin': '*',
1360
+ 'Access-Control-Allow-Methods': 'POST, OPTIONS',
1361
+ 'Access-Control-Allow-Headers': 'Content-Type'
1362
+ });
1363
+ res.end();
1364
+ return;
1365
+ }
1366
+ if ('POST' !== req.method || '/__dev_logs' !== req.url) return void next();
1367
+ let body = '';
1368
+ req.on('data', (chunk)=>{
1369
+ body += chunk.toString();
1370
+ });
1371
+ req.on('end', ()=>{
1372
+ res.writeHead(204, {
1373
+ 'Access-Control-Allow-Origin': '*'
1374
+ });
1375
+ res.end();
1376
+ try {
1377
+ const { logs } = JSON.parse(body);
1378
+ for (const entry of logs)writeLog(entry);
1379
+ } catch {}
1380
+ });
1381
+ };
1382
+ const LOG_PORT_OFFSET = 1;
1383
+ const applyDevLogger = (api)=>{
1384
+ api.modifyRspackConfig((rspackConfig)=>{
1385
+ if ('development' !== rspackConfig.mode) return rspackConfig;
1386
+ const publicPath = rspackConfig.output?.publicPath;
1387
+ let logUrl = '';
1388
+ if ('string' == typeof publicPath && publicPath.startsWith('http')) try {
1389
+ const parsed = new URL(publicPath);
1390
+ const logPort = Number(parsed.port || 80) + LOG_PORT_OFFSET;
1391
+ logUrl = `${parsed.protocol}//${parsed.hostname}:${logPort}/__dev_logs`;
1392
+ } catch {}
1393
+ if (!logUrl) logUrl = `http://localhost:${3000 + LOG_PORT_OFFSET}/__dev_logs`;
1394
+ rspackConfig.plugins = rspackConfig.plugins || [];
1395
+ rspackConfig.plugins.push({
1396
+ name: 'lynx:angular:dev-logger-define',
1397
+ apply (compiler) {
1398
+ const { DefinePlugin } = compiler.webpack;
1399
+ new DefinePlugin({
1400
+ __DEV_LOG_URL__: JSON.stringify(logUrl)
1401
+ }).apply(compiler);
1402
+ }
1403
+ });
1404
+ return rspackConfig;
1405
+ });
1406
+ api.onAfterStartDevServer(({ port })=>{
1407
+ const logPort = port + LOG_PORT_OFFSET;
1408
+ const middleware = createDevLoggerMiddleware();
1409
+ const server = node_http.createServer((req, res)=>{
1410
+ middleware(req, res, ()=>{
1411
+ res.writeHead(404);
1412
+ res.end();
1413
+ });
1414
+ });
1415
+ server.listen(logPort, '0.0.0.0', ()=>{
1416
+ console.log(`[dev-logger] Log server on :${logPort} → tail -f ${getLogFilePath()}`);
1417
+ });
1418
+ server.on('error', (err)=>{
1419
+ if ('EADDRINUSE' === err.code) server.listen(logPort + 1, '0.0.0.0');
1420
+ else console.warn('[dev-logger] Failed to start log server:', err.message);
1421
+ });
1422
+ });
1423
+ };
1424
+ const applySplitChunksRule = (api, options)=>{
1425
+ api.modifyEnvironmentConfig((config, { name, mergeEnvironmentConfig })=>{
1426
+ const userConfig = api.getRsbuildConfig('original');
1427
+ const scoped = userConfig.environments?.[name];
1428
+ const splitChunks = scoped?.splitChunks ?? userConfig.splitChunks;
1429
+ const chunkSplitStrategy = scoped?.performance?.chunkSplit?.strategy ?? userConfig.performance?.chunkSplit?.strategy;
1430
+ if (void 0 === splitChunks && (!chunkSplitStrategy || 'all-in-one' === chunkSplitStrategy)) return mergeEnvironmentConfig(config, {
1431
+ splitChunks: false
1432
+ });
1433
+ return config;
1434
+ });
1435
+ api.modifyRspackConfig((rspackConfig, { environment })=>{
1436
+ if ('lynx' !== environment.name) return rspackConfig;
1437
+ rspackConfig.output = rspackConfig.output ?? {};
1438
+ if (!options.experimental_isLazyBundle) rspackConfig.output.asyncChunks = false;
1439
+ if (!rspackConfig.optimization) return rspackConfig;
1440
+ if (!rspackConfig.optimization.splitChunks) return rspackConfig;
1441
+ const originalChunks = rspackConfig.optimization.splitChunks.chunks;
1442
+ rspackConfig.optimization.splitChunks.chunks = (chunk)=>{
1443
+ if (chunk.name?.includes('__main-thread')) return false;
1444
+ if ('function' == typeof originalChunks) return originalChunks(chunk);
1445
+ if ('async' === originalChunks) return !chunk.canBeInitial();
1446
+ if ('initial' === originalChunks) return chunk.canBeInitial();
1447
+ return true;
1448
+ };
1449
+ return rspackConfig;
1450
+ });
1451
+ };
1452
+ const TAILWIND_CONFIG_FILES = [
1453
+ 'tailwind.config.ts',
1454
+ 'tailwind.config.js',
1455
+ 'tailwind.config.mjs',
1456
+ 'tailwind.config.cjs'
1457
+ ];
1458
+ const applyTailwind = (api)=>{
1459
+ const configFile = findUp(TAILWIND_CONFIG_FILES, process.cwd());
1460
+ if (!configFile) return;
1461
+ pluginTailwindCSS({
1462
+ config: configFile,
1463
+ exclude: [
1464
+ /[\\/]node_modules[\\/]/
1465
+ ]
1466
+ }).setup(api);
1467
+ };
1468
+ const normalizeOptions = (options)=>{
1469
+ const defaultOptions = {
1470
+ customCSSInheritanceList: void 0,
1471
+ debugInfoOutside: true,
1472
+ enableA11y: true,
1473
+ enableAccessibilityElement: false,
1474
+ enableCSSInheritance: false,
1475
+ enableCSSInvalidation: false,
1476
+ enableCSSSelector: true,
1477
+ enableICU: false,
1478
+ enableNewGesture: true,
1479
+ enableParallelElement: true,
1480
+ defaultDisplayLinear: true,
1481
+ enableRemoveCSSScope: false,
1482
+ enableSSR: false,
1483
+ pages: void 0,
1484
+ pipelineSchedulerConfig: 0x00010000,
1485
+ targetSdkVersion: '3.2',
1486
+ defaultOverflowVisible: true,
1487
+ removeDescendantSelectorScope: false,
1488
+ experimental_isLazyBundle: false
1489
+ };
1490
+ return Object.assign(defaultOptions, options);
1491
+ };
1492
+ const pluginAngularLynx = (options)=>({
1493
+ name: 'lynx:angular',
1494
+ pre: [
1495
+ 'lynx:rsbuild:plugin-api'
1496
+ ],
1497
+ setup: async (api)=>{
1498
+ const normalizedOptions = normalizeOptions(options);
1499
+ applyCSS(api, normalizedOptions);
1500
+ applyTailwind(api);
1501
+ applyEntry(api, normalizedOptions);
1502
+ applyLayers(api);
1503
+ const angularRules = applyAngularRules(api, normalizedOptions);
1504
+ applyGenerator(api);
1505
+ applySplitChunksRule(api, normalizedOptions);
1506
+ applyDevLogger(api);
1507
+ await angularRules;
1508
+ }
1509
+ });
1510
+ export { pluginAngularLynx };