@blotch/rsbuild-plugin-angular-lynx 22.2.1 → 22.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -2
- package/dist/angular-cli-context.d.ts +34 -0
- package/dist/angular-cli-context.test.d.ts +1 -0
- package/dist/angular-diagnostics.d.ts +58 -0
- package/dist/angular-diagnostics.test.d.ts +1 -0
- package/dist/angular.d.ts +2 -1
- package/dist/budgets-plugin.d.ts +64 -0
- package/dist/budgets-plugin.test.d.ts +1 -0
- package/dist/component-styles-cache.d.ts +12 -0
- package/dist/entry.d.ts +5 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1223 -917
- package/dist/utils/angular/budgets.d.ts +72 -0
- package/dist/utils/angular/budgets.test.d.ts +1 -0
- package/dist/utils/angular/build-internals.d.ts +14 -0
- package/dist/utils/angular/component-style-bundler.d.ts +1 -1
- package/dist/utils/angular/options.d.ts +80 -2
- package/dist/utils/angular/read-workspace.d.ts +5 -1
- package/dist/utils/angular/resolve-primary-build.d.ts +26 -0
- package/dist/utils/angular/resolve-primary-build.test.d.ts +1 -0
- package/package.json +13 -7
- package/patch/patch-angular-build.cjs +0 -27
package/dist/index.js
CHANGED
|
@@ -1,23 +1,31 @@
|
|
|
1
1
|
/*! LICENSE: index.js.LICENSE.txt */
|
|
2
2
|
import node_fs, { existsSync } from "node:fs";
|
|
3
3
|
import node_path, { join, resolve } from "node:path";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { CSSPlugins, LynxEncodePlugin, LynxTemplatePlugin, WebEncodePlugin } from "@lynx-js/template-webpack-plugin";
|
|
5
|
+
import { RuntimeWrapperWebpackPlugin } from "@lynx-js/runtime-wrapper-webpack-plugin";
|
|
6
|
+
import { RuntimeGlobals } from "@lynx-js/webpack-runtime-globals";
|
|
7
|
+
import tiny_invariant from "tiny-invariant";
|
|
6
8
|
import { createHash } from "node:crypto";
|
|
7
9
|
import { fileURLToPath } from "node:url";
|
|
10
|
+
import { createRequire } from "node:module";
|
|
8
11
|
import { availableParallelism } from "node:os";
|
|
9
|
-
import node_assert from "node:assert";
|
|
10
|
-
import { access, constants } from "node:fs/promises";
|
|
11
12
|
import { workspaces } from "@angular-devkit/core";
|
|
12
13
|
import { NodeJsSyncHost } from "@angular-devkit/core/node";
|
|
13
|
-
import {
|
|
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";
|
|
14
|
+
import { access, constants } from "node:fs/promises";
|
|
17
15
|
import node_http from "node:http";
|
|
18
16
|
import { pluginTailwindCSS } from "rsbuild-plugin-tailwindcss";
|
|
19
17
|
import * as __rspack_external_typescript from "typescript";
|
|
20
18
|
import * as __rspack_external_node_path_c5b9b54f from "node:path";
|
|
19
|
+
const ANGULAR_CLI_CONTEXT = Symbol.for('@blotch/rsbuild-plugin-angular-lynx:angular-cli-context');
|
|
20
|
+
const ANGULAR_CLI_CONTEXT_PLUGIN_NAME = 'lynx:angular-cli-context';
|
|
21
|
+
const pluginAngularCliContext = (context)=>({
|
|
22
|
+
name: ANGULAR_CLI_CONTEXT_PLUGIN_NAME,
|
|
23
|
+
enforce: 'pre',
|
|
24
|
+
setup: (api)=>{
|
|
25
|
+
api.expose(ANGULAR_CLI_CONTEXT, context);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
const getAngularCliContext = (api)=>api.useExposed(ANGULAR_CLI_CONTEXT);
|
|
21
29
|
const TYPESCRIPT_FILE = /\.[cm]?tsx?$/;
|
|
22
30
|
const readFileVersion = (file)=>node_fs.statSync(file, {
|
|
23
31
|
throwIfNoEntry: false
|
|
@@ -76,200 +84,817 @@ const createAngularRebuildCoordinator = ({ compile, getFileVersion = readFileVer
|
|
|
76
84
|
}
|
|
77
85
|
};
|
|
78
86
|
};
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
};
|
|
87
|
+
const LAYERS = {
|
|
88
|
+
BACKGROUND: 'background',
|
|
89
|
+
MAIN_THREAD: 'main'
|
|
117
90
|
};
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
91
|
+
const applyLayers = (api)=>{
|
|
92
|
+
api.modifyBundlerChain((chain)=>{
|
|
93
|
+
chain.module.rule("typescript").oneOf(LAYERS.BACKGROUND).layer(LAYERS.BACKGROUND).test(/\.[cm]?[jt]sx?$/).use('builtin:swc-loader').loader('builtin:swc-loader').options({
|
|
94
|
+
jsc: {
|
|
95
|
+
target: 'es2015',
|
|
96
|
+
parser: {
|
|
97
|
+
syntax: "typescript"
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}).end();
|
|
101
|
+
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({
|
|
102
|
+
jsc: {
|
|
103
|
+
target: 'es2019',
|
|
104
|
+
parser: {
|
|
105
|
+
syntax: "typescript"
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}).end();
|
|
109
|
+
});
|
|
121
110
|
};
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
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`);
|
|
111
|
+
const createLynxProcessEvalResultRuntimeModule = (webpack)=>class extends webpack.RuntimeModule {
|
|
112
|
+
constructor(){
|
|
113
|
+
super('Lynx process eval result', webpack.RuntimeModule.STAGE_ATTACH);
|
|
159
114
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
const
|
|
163
|
-
if (
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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
|
-
});
|
|
115
|
+
generate() {
|
|
116
|
+
const chunk = this.chunk;
|
|
117
|
+
const compilation = this.compilation;
|
|
118
|
+
if (!chunk || !compilation) return '';
|
|
119
|
+
const chunkGroup = chunk._groupsIterable?.[0];
|
|
120
|
+
const modules = compilation.chunkGraph?.getChunkModules(chunk) ?? [];
|
|
121
|
+
modules.sort((a, b)=>{
|
|
122
|
+
const aIdx = chunkGroup?.getModulePreOrderIndex(a) ?? 0;
|
|
123
|
+
const bIdx = chunkGroup?.getModulePreOrderIndex(b) ?? 0;
|
|
124
|
+
return aIdx - bIdx;
|
|
125
|
+
});
|
|
126
|
+
const sortedIds = modules.map((m)=>compilation.chunkGraph.getModuleId(m)).filter((id)=>null != id);
|
|
127
|
+
const moduleOrder = JSON.stringify(sortedIds);
|
|
128
|
+
return `
|
|
129
|
+
${RuntimeGlobals.lynxProcessEvalResult} = function (result, schema) {
|
|
130
|
+
var chunk = result && result(schema);
|
|
131
|
+
if (chunk && chunk.ids && chunk.modules) {
|
|
132
|
+
${webpack.RuntimeGlobals.externalInstallChunk}(chunk);
|
|
133
|
+
var moduleOrder = ${moduleOrder};
|
|
134
|
+
for (var i = 0; i < moduleOrder.length; i++) {
|
|
135
|
+
if (chunk.modules[moduleOrder[i]]) {
|
|
136
|
+
${webpack.RuntimeGlobals.require}(moduleOrder[i]);
|
|
137
|
+
}
|
|
203
138
|
}
|
|
204
|
-
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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}";`;
|
|
139
|
+
return chunk;
|
|
140
|
+
}
|
|
141
|
+
return chunk
|
|
142
|
+
}
|
|
143
|
+
`;
|
|
235
144
|
}
|
|
236
|
-
|
|
145
|
+
};
|
|
146
|
+
class AngularWebpackPlugin {
|
|
147
|
+
options;
|
|
148
|
+
constructor(options){
|
|
149
|
+
this.options = options;
|
|
237
150
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
};
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
const
|
|
151
|
+
static defaultOptions = Object.freeze({
|
|
152
|
+
disableCreateSelectorQueryIncompatibleWarning: false,
|
|
153
|
+
firstScreenSyncTiming: 'immediately',
|
|
154
|
+
enableSSR: false,
|
|
155
|
+
isWeb: false,
|
|
156
|
+
mainThreadChunks: [],
|
|
157
|
+
extractStr: false,
|
|
158
|
+
experimental_isLazyBundle: false
|
|
159
|
+
});
|
|
160
|
+
apply(compiler) {
|
|
161
|
+
const options = Object.assign({}, AngularWebpackPlugin.defaultOptions, this.options);
|
|
162
|
+
if (options.enableSSR && 'jsReady' !== options.firstScreenSyncTiming) options.firstScreenSyncTiming = 'jsReady';
|
|
163
|
+
const { DefinePlugin, EnvironmentPlugin } = compiler.webpack;
|
|
164
|
+
new EnvironmentPlugin({
|
|
165
|
+
DEBUG: null
|
|
166
|
+
}).apply(compiler);
|
|
167
|
+
new DefinePlugin({
|
|
168
|
+
__DEV__: JSON.stringify('development' === compiler.options.mode),
|
|
169
|
+
__PROFILE__: JSON.stringify('development' === compiler.options.mode),
|
|
170
|
+
__EXTRACT_STR__: JSON.stringify(Boolean(options.extractStr)),
|
|
171
|
+
__FIRST_SCREEN_SYNC_TIMING__: JSON.stringify(options.firstScreenSyncTiming),
|
|
172
|
+
__ENABLE_SSR__: JSON.stringify(options.enableSSR),
|
|
173
|
+
__WEB__: JSON.stringify(Boolean(options.isWeb)),
|
|
174
|
+
__DISABLE_CREATE_SELECTOR_QUERY_INCOMPATIBLE_WARNING__: JSON.stringify(options.disableCreateSelectorQueryIncompatibleWarning)
|
|
175
|
+
}).apply(compiler);
|
|
176
|
+
compiler.hooks.thisCompilation.tap(this.constructor.name, (compilation)=>{
|
|
177
|
+
const onceForChunkSet = new WeakSet();
|
|
178
|
+
compilation.hooks.runtimeRequirementInTree.for(compiler.webpack.RuntimeGlobals.ensureChunkHandlers).tap('VanillaWebpackPlugin', (chunk, runtimeRequirements)=>{
|
|
179
|
+
runtimeRequirements.add(RuntimeGlobals.lynxProcessEvalResult);
|
|
180
|
+
});
|
|
181
|
+
compilation.hooks.runtimeRequirementInTree.for(RuntimeGlobals.lynxProcessEvalResult).tap('VanillaWebpackPlugin', (chunk)=>{
|
|
182
|
+
if (onceForChunkSet.has(chunk)) return;
|
|
183
|
+
onceForChunkSet.add(chunk);
|
|
184
|
+
const LynxProcessEvalResultRuntimeModule = createLynxProcessEvalResultRuntimeModule(compiler.webpack);
|
|
185
|
+
compilation.addRuntimeModule(chunk, new LynxProcessEvalResultRuntimeModule());
|
|
186
|
+
});
|
|
187
|
+
compilation.hooks.processAssets.tap({
|
|
188
|
+
name: this.constructor.name,
|
|
189
|
+
stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL
|
|
190
|
+
}, ()=>{
|
|
191
|
+
for (const name of options.mainThreadChunks ?? [])this.#updateMainThreadInfo(compilation, name);
|
|
192
|
+
for (const cg of compilation.chunkGroups.filter((cg)=>!cg.isInitial()).filter((cg)=>cg.origins.every((origin)=>origin.module?.layer === LAYERS.MAIN_THREAD))){
|
|
193
|
+
const files = cg.getFiles();
|
|
194
|
+
for (const name of files.filter((name)=>name.endsWith('.js')))this.#updateMainThreadInfo(compilation, name);
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
{
|
|
198
|
+
const { ConcatSource } = compiler.webpack.sources;
|
|
199
|
+
const mainThreadChunkSet = new Set(options.mainThreadChunks ?? []);
|
|
200
|
+
compilation.hooks.processAssets.tap({
|
|
201
|
+
name: `${this.constructor.name}:threadGlobals`,
|
|
202
|
+
stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONS
|
|
203
|
+
}, ()=>{
|
|
204
|
+
for (const [name] of Object.entries(compilation.assets).filter(([n])=>n.endsWith('.js'))){
|
|
205
|
+
const isMainThread = mainThreadChunkSet.has(name);
|
|
206
|
+
const prefix = `globalThis["__MAIN_THREAD__"]=${isMainThread};`;
|
|
207
|
+
compilation.updateAsset(name, (old)=>new ConcatSource(prefix, old));
|
|
208
|
+
}
|
|
209
|
+
if (!options.experimental_isLazyBundle) for (const name of options.mainThreadChunks ?? []){
|
|
210
|
+
const asset = compilation.getAsset(name);
|
|
211
|
+
if (!asset) continue;
|
|
212
|
+
const source = asset.source.source().toString();
|
|
213
|
+
const useStrictPrefix = /^(['"])use strict\1;?/.test(source) ? "'use strict';" : '';
|
|
214
|
+
compilation.updateAsset(name, (old)=>new ConcatSource(`${useStrictPrefix}var globDynamicComponentEntry=globDynamicComponentEntry||'__Card__';`, old));
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
const hooks = LynxTemplatePlugin.getLynxTemplatePluginHooks(compilation);
|
|
219
|
+
const { ConcatSource } = compiler.webpack.sources;
|
|
220
|
+
hooks.beforeEncode.tap(this.constructor.name, (args)=>{
|
|
221
|
+
const { encodeData } = args;
|
|
222
|
+
encodeData.sourceContent.config = {
|
|
223
|
+
...encodeData.sourceContent.config,
|
|
224
|
+
enableCSSInlineVariables: true
|
|
225
|
+
};
|
|
226
|
+
if (!encodeData.lepusCode.root) return args;
|
|
227
|
+
if ('card' === encodeData.sourceContent.appType) return args;
|
|
228
|
+
compilation.updateAsset(encodeData.lepusCode.root.name, (old)=>new ConcatSource(`\
|
|
229
|
+
(function (globDynamicComponentEntry) {
|
|
230
|
+
const module = { exports: {} }
|
|
231
|
+
const exports = module.exports
|
|
232
|
+
`, old, `
|
|
233
|
+
;return module.exports
|
|
234
|
+
})`));
|
|
235
|
+
return args;
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
#updateMainThreadInfo(compilation, name) {
|
|
240
|
+
const asset = compilation.getAsset(name);
|
|
241
|
+
tiny_invariant(asset, `Should have main thread asset ${name}`);
|
|
242
|
+
compilation.updateAsset(asset.name, asset.source, {
|
|
243
|
+
...asset.info,
|
|
244
|
+
'lynx:main-thread': true
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
const S_LYNX_CONFIG = Symbol.for('@lynx-js/rsbuild-plugin:config');
|
|
249
|
+
const PLUGIN_NAME_TEMPLATE = 'lynx:template';
|
|
250
|
+
const PLUGIN_NAME_RUNTIME_WRAPPER = 'lynx:runtime-wrapper';
|
|
251
|
+
const PLUGIN_NAME_ANGULAR = 'lynx:angular';
|
|
252
|
+
const applyEntry = (api, options)=>{
|
|
253
|
+
const { customCSSInheritanceList, debugInfoOutside, defaultDisplayLinear, enableA11y, enableAccessibilityElement, enableCSSInheritance, enableCSSInvalidation, enableCSSSelector, enableNewGesture, enableRemoveCSSScope, removeDescendantSelectorScope, targetSdkVersion } = options;
|
|
254
|
+
api.modifyBundlerChain((chain, { environment, isDev })=>{
|
|
255
|
+
const lynxConfig = api.useExposed(S_LYNX_CONFIG);
|
|
256
|
+
if (!lynxConfig) throw new Error('No Lynx config exposed. `pluginLynx` has to be applied for the Lynx build engine to be configured.');
|
|
257
|
+
const isLynx = 'lynx' === environment.name;
|
|
258
|
+
const isWeb = 'web' === environment.name;
|
|
259
|
+
const { hmr, liveReload } = environment.config.dev ?? {};
|
|
260
|
+
const enabledHMR = isDev && !isWeb && false !== hmr;
|
|
261
|
+
const enabledLiveReload = isDev && !isWeb && false !== liveReload;
|
|
262
|
+
const entries = chain.entryPoints.entries() ?? {};
|
|
263
|
+
chain.entryPoints.clear();
|
|
264
|
+
const mainThreadChunks = [];
|
|
265
|
+
for (const [entryName, entryPoint] of Object.entries(entries)){
|
|
266
|
+
const { imports } = getChunks(entryName, entryPoint.values());
|
|
267
|
+
const templateFilename = lynxConfig.resolveBundleFilename({
|
|
268
|
+
entryName,
|
|
269
|
+
platform: environment.name
|
|
270
|
+
});
|
|
271
|
+
const lazyBundleFilename = lynxConfig.resolveLazyBundleFilename({
|
|
272
|
+
platform: environment.name
|
|
273
|
+
});
|
|
274
|
+
const mainThreadEntry = `${entryName}__main-thread`;
|
|
275
|
+
const mainThreadName = node_path.posix.join(`${entryName}/main-thread.js`);
|
|
276
|
+
const backgroundEntry = entryName;
|
|
277
|
+
const backgroundThreadName = node_path.posix.join(`${entryName}/background-thread.js`);
|
|
278
|
+
mainThreadChunks.push(mainThreadName);
|
|
279
|
+
chain.entry(mainThreadEntry).add({
|
|
280
|
+
layer: LAYERS.MAIN_THREAD,
|
|
281
|
+
import: imports,
|
|
282
|
+
filename: mainThreadName
|
|
283
|
+
}).end();
|
|
284
|
+
chain.entry(backgroundEntry).add({
|
|
285
|
+
layer: LAYERS.BACKGROUND,
|
|
286
|
+
import: imports,
|
|
287
|
+
filename: backgroundThreadName
|
|
288
|
+
}).when(enabledHMR, (entry)=>{
|
|
289
|
+
entry.prepend({
|
|
290
|
+
layer: LAYERS.BACKGROUND,
|
|
291
|
+
import: '@rspack/core/hot/dev-server'
|
|
292
|
+
});
|
|
293
|
+
}).when(enabledHMR || enabledLiveReload, (entry)=>{
|
|
294
|
+
entry.prepend({
|
|
295
|
+
layer: LAYERS.BACKGROUND,
|
|
296
|
+
import: '@lynx-js/webpack-dev-transport/client'
|
|
297
|
+
});
|
|
298
|
+
}).end();
|
|
299
|
+
chain.plugin(`${PLUGIN_NAME_TEMPLATE}-${entryName}`).use(LynxTemplatePlugin, [
|
|
300
|
+
{
|
|
301
|
+
filename: templateFilename,
|
|
302
|
+
...lazyBundleFilename ? {
|
|
303
|
+
lazyBundleFilename
|
|
304
|
+
} : {},
|
|
305
|
+
intermediate: lynxConfig.resolveIntermediateDir({
|
|
306
|
+
entryName
|
|
307
|
+
}),
|
|
308
|
+
chunks: [
|
|
309
|
+
mainThreadEntry,
|
|
310
|
+
backgroundEntry
|
|
311
|
+
],
|
|
312
|
+
cssPlugins: [
|
|
313
|
+
CSSPlugins.parserPlugins.removeFunctionWhiteSpace()
|
|
314
|
+
],
|
|
315
|
+
customCSSInheritanceList,
|
|
316
|
+
debugInfoOutside,
|
|
317
|
+
defaultDisplayLinear,
|
|
318
|
+
enableAccessibilityElement,
|
|
319
|
+
enableCSSInheritance,
|
|
320
|
+
enableCSSInvalidation,
|
|
321
|
+
enableCSSSelector,
|
|
322
|
+
enableNewGesture,
|
|
323
|
+
enableRemoveCSSScope,
|
|
324
|
+
removeDescendantSelectorScope,
|
|
325
|
+
targetSdkVersion,
|
|
326
|
+
enableA11y
|
|
327
|
+
}
|
|
328
|
+
]).end();
|
|
329
|
+
}
|
|
330
|
+
if (isLynx) {
|
|
331
|
+
chain.plugin(PLUGIN_NAME_RUNTIME_WRAPPER).use(RuntimeWrapperWebpackPlugin, [
|
|
332
|
+
{
|
|
333
|
+
injectVars (vars) {
|
|
334
|
+
return vars.map((name)=>{
|
|
335
|
+
if ('Component' === name) return '__Component';
|
|
336
|
+
return name;
|
|
337
|
+
});
|
|
338
|
+
},
|
|
339
|
+
targetSdkVersion,
|
|
340
|
+
test: /^(?!.*main-thread(?:\.[A-Fa-f0-9]*)?\.js$).*\.js$/
|
|
341
|
+
}
|
|
342
|
+
]).end();
|
|
343
|
+
chain.plugin(`${LynxEncodePlugin.name}`).use(LynxEncodePlugin, [
|
|
344
|
+
{}
|
|
345
|
+
]).end();
|
|
346
|
+
}
|
|
347
|
+
if (isWeb) chain.plugin(`${WebEncodePlugin.name}`).use(WebEncodePlugin, []).end();
|
|
348
|
+
chain.plugin(PLUGIN_NAME_ANGULAR).after(PLUGIN_NAME_TEMPLATE).use(AngularWebpackPlugin, [
|
|
349
|
+
{
|
|
350
|
+
mainThreadChunks,
|
|
351
|
+
enableSSR: options.enableSSR,
|
|
352
|
+
isWeb
|
|
353
|
+
}
|
|
354
|
+
]);
|
|
355
|
+
});
|
|
356
|
+
};
|
|
357
|
+
const getChunks = (entryName, entryValue)=>{
|
|
358
|
+
const chunks = [
|
|
359
|
+
entryName
|
|
360
|
+
];
|
|
361
|
+
const imports = [];
|
|
362
|
+
for (const item of entryValue){
|
|
363
|
+
if ('string' == typeof item) {
|
|
364
|
+
imports.push(item);
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
if (Array.isArray(item)) {
|
|
368
|
+
imports.push(...imports);
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
const { dependOn } = item;
|
|
372
|
+
if (Array.isArray(item.import)) imports.push(...item.import);
|
|
373
|
+
else imports.push(item.import);
|
|
374
|
+
if (dependOn) if ('string' == typeof dependOn) chunks.unshift(dependOn);
|
|
375
|
+
else chunks.unshift(...dependOn);
|
|
376
|
+
}
|
|
377
|
+
return {
|
|
378
|
+
chunks,
|
|
379
|
+
imports
|
|
380
|
+
};
|
|
381
|
+
};
|
|
382
|
+
const BYTES_IN_KILOBYTE = 1000;
|
|
383
|
+
const calculateBytes = (input, baseline, factor = 1)=>{
|
|
384
|
+
const matches = input.trim().match(/^(\d+(?:\.\d+)?)[ \t]*(%|[kmg]?b)?$/i);
|
|
385
|
+
if (!matches) return NaN;
|
|
386
|
+
const baselineBytes = baseline && calculateBytes(baseline) || 0;
|
|
387
|
+
let value = Number(matches[1]);
|
|
388
|
+
switch(matches[2]?.toLowerCase()){
|
|
389
|
+
case '%':
|
|
390
|
+
value = baselineBytes * value / 100;
|
|
391
|
+
break;
|
|
392
|
+
case 'kb':
|
|
393
|
+
value *= BYTES_IN_KILOBYTE;
|
|
394
|
+
break;
|
|
395
|
+
case 'mb':
|
|
396
|
+
value *= BYTES_IN_KILOBYTE * BYTES_IN_KILOBYTE;
|
|
397
|
+
break;
|
|
398
|
+
case 'gb':
|
|
399
|
+
value *= BYTES_IN_KILOBYTE * BYTES_IN_KILOBYTE * BYTES_IN_KILOBYTE;
|
|
400
|
+
break;
|
|
401
|
+
}
|
|
402
|
+
if (0 === baselineBytes) return value;
|
|
403
|
+
return baselineBytes + value * factor;
|
|
404
|
+
};
|
|
405
|
+
const calculateThresholds = (budget)=>{
|
|
406
|
+
const thresholds = [];
|
|
407
|
+
if (budget.maximumWarning) thresholds.push({
|
|
408
|
+
limit: calculateBytes(budget.maximumWarning, budget.baseline, 1),
|
|
409
|
+
type: 'maximum',
|
|
410
|
+
severity: 'warning'
|
|
411
|
+
});
|
|
412
|
+
if (budget.maximumError) thresholds.push({
|
|
413
|
+
limit: calculateBytes(budget.maximumError, budget.baseline, 1),
|
|
414
|
+
type: 'maximum',
|
|
415
|
+
severity: 'error'
|
|
416
|
+
});
|
|
417
|
+
if (budget.minimumWarning) thresholds.push({
|
|
418
|
+
limit: calculateBytes(budget.minimumWarning, budget.baseline, -1),
|
|
419
|
+
type: 'minimum',
|
|
420
|
+
severity: 'warning'
|
|
421
|
+
});
|
|
422
|
+
if (budget.minimumError) thresholds.push({
|
|
423
|
+
limit: calculateBytes(budget.minimumError, budget.baseline, -1),
|
|
424
|
+
type: 'minimum',
|
|
425
|
+
severity: 'error'
|
|
426
|
+
});
|
|
427
|
+
if (budget.warning) thresholds.push({
|
|
428
|
+
limit: calculateBytes(budget.warning, budget.baseline, -1),
|
|
429
|
+
type: 'minimum',
|
|
430
|
+
severity: 'warning'
|
|
431
|
+
}, {
|
|
432
|
+
limit: calculateBytes(budget.warning, budget.baseline, 1),
|
|
433
|
+
type: 'maximum',
|
|
434
|
+
severity: 'warning'
|
|
435
|
+
});
|
|
436
|
+
if (budget.error) thresholds.push({
|
|
437
|
+
limit: calculateBytes(budget.error, budget.baseline, -1),
|
|
438
|
+
type: 'minimum',
|
|
439
|
+
severity: 'error'
|
|
440
|
+
}, {
|
|
441
|
+
limit: calculateBytes(budget.error, budget.baseline, 1),
|
|
442
|
+
type: 'maximum',
|
|
443
|
+
severity: 'error'
|
|
444
|
+
});
|
|
445
|
+
return thresholds;
|
|
446
|
+
};
|
|
447
|
+
const formatSize = (size)=>{
|
|
448
|
+
if (size <= 0) return '0 bytes';
|
|
449
|
+
const abbreviations = [
|
|
450
|
+
'bytes',
|
|
451
|
+
'kB',
|
|
452
|
+
'MB',
|
|
453
|
+
'GB'
|
|
454
|
+
];
|
|
455
|
+
const index = Math.floor(Math.log(size) / Math.log(BYTES_IN_KILOBYTE));
|
|
456
|
+
const roundedSize = size / Math.pow(BYTES_IN_KILOBYTE, index);
|
|
457
|
+
const fractionDigits = 0 === index ? 0 : 2;
|
|
458
|
+
return `${roundedSize.toFixed(fractionDigits)} ${abbreviations[index]}`;
|
|
459
|
+
};
|
|
460
|
+
const checkThresholds = (thresholds, size, label)=>thresholds.flatMap(({ limit, type, severity })=>{
|
|
461
|
+
if ('maximum' === type) return size <= limit ? [] : [
|
|
462
|
+
{
|
|
463
|
+
severity,
|
|
464
|
+
label,
|
|
465
|
+
message: `${label} exceeded maximum budget. Budget ${formatSize(limit)} was not met by ${formatSize(size - limit)} with a total of ${formatSize(size)}.`
|
|
466
|
+
}
|
|
467
|
+
];
|
|
468
|
+
return size >= limit ? [] : [
|
|
469
|
+
{
|
|
470
|
+
severity,
|
|
471
|
+
label,
|
|
472
|
+
message: `${label} failed to meet minimum budget. Budget ${formatSize(limit)} was not met by ${formatSize(limit - size)} with a total of ${formatSize(size)}.`
|
|
473
|
+
}
|
|
474
|
+
];
|
|
475
|
+
});
|
|
476
|
+
const sum = (sizes)=>sizes.reduce((total, { size })=>total + size, 0);
|
|
477
|
+
const calculateSizes = (budget, assets, componentStyles)=>{
|
|
478
|
+
switch(budget.type){
|
|
479
|
+
case 'initial':
|
|
480
|
+
{
|
|
481
|
+
const bundles = assets.filter((asset)=>void 0 !== asset.entryName);
|
|
482
|
+
return bundles.map((asset)=>({
|
|
483
|
+
size: asset.size,
|
|
484
|
+
label: 1 === bundles.length ? 'bundle initial' : `bundle initial (${asset.entryName})`
|
|
485
|
+
}));
|
|
486
|
+
}
|
|
487
|
+
case 'bundle':
|
|
488
|
+
return budget.name ? [
|
|
489
|
+
{
|
|
490
|
+
size: sum(assets.filter((asset)=>asset.entryName === budget.name)),
|
|
491
|
+
label: budget.name
|
|
492
|
+
}
|
|
493
|
+
] : [];
|
|
494
|
+
case 'all':
|
|
495
|
+
return [
|
|
496
|
+
{
|
|
497
|
+
size: sum(assets),
|
|
498
|
+
label: 'total'
|
|
499
|
+
}
|
|
500
|
+
];
|
|
501
|
+
case 'allScript':
|
|
502
|
+
return [
|
|
503
|
+
{
|
|
504
|
+
size: sum(assets),
|
|
505
|
+
label: "total scripts"
|
|
506
|
+
}
|
|
507
|
+
];
|
|
508
|
+
case 'any':
|
|
509
|
+
case 'anyScript':
|
|
510
|
+
return assets.map((asset)=>({
|
|
511
|
+
size: asset.size,
|
|
512
|
+
label: asset.name
|
|
513
|
+
}));
|
|
514
|
+
case 'anyComponentStyle':
|
|
515
|
+
return componentStyles.map((style)=>({
|
|
516
|
+
...style
|
|
517
|
+
}));
|
|
518
|
+
default:
|
|
519
|
+
return [];
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
const checkBudgets = (budgets, output)=>budgets.flatMap((budget)=>calculateSizes(budget, output.assets, output.componentStyles).flatMap(({ size, label })=>checkThresholds(calculateThresholds(budget), size, label)));
|
|
523
|
+
const PLUGIN_NAME = 'lynx:angular:budgets';
|
|
524
|
+
const MAIN_THREAD_ENTRY_SUFFIX = '__main-thread';
|
|
525
|
+
const toBudgetError = (failure)=>{
|
|
526
|
+
const error = new Error(failure.message);
|
|
527
|
+
error.name = 'error' === failure.severity ? 'AngularBudgetError' : 'AngularBudgetWarning';
|
|
528
|
+
error.hideStack = true;
|
|
529
|
+
return error;
|
|
530
|
+
};
|
|
531
|
+
const bundleEntryName = (chunkGroups, entrypoints)=>chunkGroups.map((chunkGroup)=>chunkGroup.name).find((name)=>!!name && entrypoints.has(name) && !name.endsWith(MAIN_THREAD_ENTRY_SUFFIX));
|
|
532
|
+
const collectBudgetAssets = (assets, entryBundles, intermediateDir)=>assets.filter(({ name, info })=>/\.(bundle|js)$/.test(name) && !info.hotModuleReplacement && !(intermediateDir && name.startsWith(`${intermediateDir}/`))).map(({ name, source })=>{
|
|
533
|
+
const entryName = entryBundles.get(name);
|
|
534
|
+
return {
|
|
535
|
+
name,
|
|
536
|
+
size: source.size(),
|
|
537
|
+
...void 0 === entryName ? {} : {
|
|
538
|
+
entryName
|
|
539
|
+
}
|
|
540
|
+
};
|
|
541
|
+
});
|
|
542
|
+
class AngularBudgetsPlugin {
|
|
543
|
+
#options;
|
|
544
|
+
constructor(options){
|
|
545
|
+
this.#options = options;
|
|
546
|
+
}
|
|
547
|
+
apply(compiler) {
|
|
548
|
+
const { budgets, componentStyleSizes, intermediateDir } = this.#options;
|
|
549
|
+
compiler.hooks.thisCompilation.tap(PLUGIN_NAME, (compilation)=>{
|
|
550
|
+
const entryBundles = new Map();
|
|
551
|
+
LynxTemplatePlugin.getLynxTemplatePluginHooks(compilation).beforeEmit.tap(PLUGIN_NAME, (args)=>{
|
|
552
|
+
const entryName = bundleEntryName(args.chunkGroups, compilation.entrypoints);
|
|
553
|
+
if (void 0 !== entryName) entryBundles.set(args.outputName, entryName);
|
|
554
|
+
return args;
|
|
555
|
+
});
|
|
556
|
+
compilation.hooks.processAssets.tap({
|
|
557
|
+
name: PLUGIN_NAME,
|
|
558
|
+
stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_REPORT + 2
|
|
559
|
+
}, ()=>{
|
|
560
|
+
const failures = checkBudgets(budgets, {
|
|
561
|
+
assets: collectBudgetAssets(compilation.getAssets(), entryBundles, intermediateDir),
|
|
562
|
+
componentStyles: [
|
|
563
|
+
...componentStyleSizes.values()
|
|
564
|
+
]
|
|
565
|
+
});
|
|
566
|
+
for (const failure of failures)('error' === failure.severity ? compilation.errors : compilation.warnings).push(toBudgetError(failure));
|
|
567
|
+
});
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
const applyBudgets = (api, { budgets, componentStyleSizes, underAngularCli })=>{
|
|
572
|
+
if (0 === budgets.length) return;
|
|
573
|
+
if (!underAngularCli && 'dev' === api.context.action) return;
|
|
574
|
+
api.modifyBundlerChain((chain, { environment })=>{
|
|
575
|
+
if ('lynx' !== environment.name) return;
|
|
576
|
+
const lynxConfig = api.useExposed(S_LYNX_CONFIG);
|
|
577
|
+
chain.plugin(PLUGIN_NAME).use(AngularBudgetsPlugin, [
|
|
578
|
+
{
|
|
579
|
+
budgets,
|
|
580
|
+
componentStyleSizes,
|
|
581
|
+
intermediateDir: lynxConfig?.resolveIntermediateDir()
|
|
582
|
+
}
|
|
583
|
+
]);
|
|
584
|
+
});
|
|
585
|
+
};
|
|
586
|
+
const injectLynxSchema = (source)=>{
|
|
587
|
+
if (source.includes('CUSTOM_ELEMENTS_SCHEMA') || source.includes('NO_ERRORS_SCHEMA')) return source;
|
|
588
|
+
const importLine = `import { CUSTOM_ELEMENTS_SCHEMA as __LynxCES__ } from '@angular/core';\n`;
|
|
589
|
+
const patched = source.replace(/@Component\s*\(\s*\{/g, '@Component({ schemas: [__LynxCES__],');
|
|
590
|
+
if (patched === source) return source;
|
|
591
|
+
return importLine + patched;
|
|
592
|
+
};
|
|
593
|
+
const stripTemplateWhitespace = (source)=>source.replace(/(?<=>)[ \t]*\n[ \t]*([^\n<]+?)[ \t]*\n[ \t]*(?=<)/g, '$1');
|
|
594
|
+
const buildLynxSchemaSourceFileCache = (tsconfig, shouldInclude = ()=>true)=>{
|
|
595
|
+
const sourceFileCache = new Map();
|
|
596
|
+
let fileNames;
|
|
597
|
+
try {
|
|
598
|
+
const configFile = __rspack_external_typescript.readConfigFile(tsconfig, (p)=>node_fs.readFileSync(p, 'utf-8'));
|
|
599
|
+
const parsedConfig = __rspack_external_typescript.parseJsonConfigFileContent(configFile.config, __rspack_external_typescript.sys, node_path.dirname(tsconfig));
|
|
600
|
+
fileNames = parsedConfig.fileNames;
|
|
601
|
+
} catch {
|
|
602
|
+
return {
|
|
603
|
+
sourceFileCache,
|
|
604
|
+
fileNames: []
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
for (const filePath of fileNames){
|
|
608
|
+
if (filePath.includes('node_modules')) continue;
|
|
609
|
+
if (!shouldInclude(filePath)) continue;
|
|
610
|
+
let source;
|
|
611
|
+
try {
|
|
612
|
+
source = node_fs.readFileSync(filePath, 'utf-8');
|
|
613
|
+
} catch {
|
|
614
|
+
continue;
|
|
615
|
+
}
|
|
616
|
+
if (!source.includes('@Component')) continue;
|
|
617
|
+
const transformed = stripTemplateWhitespace(injectLynxSchema(source));
|
|
618
|
+
sourceFileCache.set(filePath, __rspack_external_typescript.createSourceFile(filePath, transformed, __rspack_external_typescript.ScriptTarget.Latest, true));
|
|
619
|
+
}
|
|
620
|
+
return {
|
|
621
|
+
sourceFileCache,
|
|
622
|
+
fileNames
|
|
623
|
+
};
|
|
624
|
+
};
|
|
625
|
+
const generateComponentScopeId = (className, containingFile)=>{
|
|
626
|
+
const hash = createHash('sha256').update(`${className}|${containingFile}`).digest('hex').slice(0, 10);
|
|
627
|
+
return `l${hash}`;
|
|
628
|
+
};
|
|
629
|
+
const componentStyleSizeKey = (containingFile, stylesheetKey)=>`${containingFile}#${stylesheetKey}`;
|
|
630
|
+
const forgetComponentStyleSizes = (componentStyleSizes, containingFile)=>{
|
|
631
|
+
const prefix = componentStyleSizeKey(containingFile, '');
|
|
632
|
+
for (const key of componentStyleSizes.keys())if (key.startsWith(prefix)) componentStyleSizes.delete(key);
|
|
633
|
+
};
|
|
634
|
+
const createTransformStylesheet = (params)=>{
|
|
635
|
+
const { basePath, scopedCssCacheDir, componentStylesCache, componentScopeIds, onStylesheetWritten, componentStyleSizes } = params;
|
|
636
|
+
return async (data, containingFile, stylesheetFile, order, className)=>{
|
|
637
|
+
const resolvedClassName = className ?? 'Component';
|
|
638
|
+
const scopeId = generateComponentScopeId(resolvedClassName, containingFile);
|
|
639
|
+
let componentStyles = componentStylesCache.get(containingFile);
|
|
640
|
+
if (!componentStyles) {
|
|
641
|
+
componentStyles = {
|
|
642
|
+
imports: [],
|
|
643
|
+
inlineStyles: [],
|
|
644
|
+
scopeId,
|
|
645
|
+
processedFiles: new Map()
|
|
646
|
+
};
|
|
647
|
+
componentStylesCache.set(containingFile, componentStyles);
|
|
648
|
+
}
|
|
649
|
+
const stylesheetKey = stylesheetFile ?? `__inline_${order}`;
|
|
650
|
+
const previousPath = componentStyles.processedFiles.get(stylesheetKey);
|
|
651
|
+
if (previousPath && !className) return '';
|
|
652
|
+
componentStyleSizes?.set(componentStyleSizeKey(containingFile, stylesheetKey), {
|
|
653
|
+
label: node_path.relative(basePath, stylesheetFile ?? containingFile).replace(/\\/g, '/'),
|
|
654
|
+
size: Buffer.byteLength(data)
|
|
655
|
+
});
|
|
656
|
+
if (className || !componentScopeIds.has(containingFile)) componentScopeIds.set(containingFile, {
|
|
657
|
+
className: resolvedClassName,
|
|
658
|
+
scopeId
|
|
659
|
+
});
|
|
660
|
+
const scopedCss = data;
|
|
661
|
+
const writeIfChanged = (filePath, content)=>{
|
|
662
|
+
try {
|
|
663
|
+
if (node_fs.readFileSync(filePath, 'utf-8') === content) return;
|
|
664
|
+
} catch {}
|
|
665
|
+
node_fs.writeFileSync(filePath, content);
|
|
666
|
+
onStylesheetWritten?.(filePath);
|
|
667
|
+
};
|
|
668
|
+
let scopedPath;
|
|
669
|
+
if (stylesheetFile) {
|
|
670
|
+
const relName = node_path.relative(basePath, stylesheetFile);
|
|
671
|
+
scopedPath = node_path.join(scopedCssCacheDir, `${relName.replace(/[/\\]/g, '__')}.__scoped_${scopeId}.css`);
|
|
672
|
+
} else {
|
|
673
|
+
const relDir = node_path.relative(basePath, node_path.dirname(containingFile));
|
|
674
|
+
scopedPath = node_path.join(scopedCssCacheDir, `${relDir.replace(/[/\\]/g, '__')}__inline_${resolvedClassName}_${order}.__scoped_${scopeId}.css`);
|
|
675
|
+
}
|
|
676
|
+
writeIfChanged(scopedPath, scopedCss);
|
|
677
|
+
if (previousPath) {
|
|
678
|
+
const idx = componentStyles.imports.indexOf(previousPath);
|
|
679
|
+
if (idx >= 0) componentStyles.imports[idx] = scopedPath;
|
|
680
|
+
else componentStyles.imports.push(scopedPath);
|
|
681
|
+
if (previousPath !== scopedPath) try {
|
|
682
|
+
node_fs.unlinkSync(previousPath);
|
|
683
|
+
} catch {}
|
|
684
|
+
} else componentStyles.imports.push(scopedPath);
|
|
685
|
+
componentStyles.processedFiles.set(stylesheetKey, scopedPath);
|
|
686
|
+
return '';
|
|
687
|
+
};
|
|
688
|
+
};
|
|
689
|
+
const isLynxUnknownElementMessage = (text)=>(text?.includes('is not a known element') || text?.includes("isn't a known property of")) ?? false;
|
|
690
|
+
const filterAngularDiagnostics = (diagnostics)=>({
|
|
691
|
+
errors: (diagnostics.errors ?? []).filter((message)=>!isLynxUnknownElementMessage(message.text)),
|
|
692
|
+
warnings: (diagnostics.warnings ?? []).filter((message)=>!isLynxUnknownElementMessage(message.text))
|
|
693
|
+
});
|
|
694
|
+
const formatLocation = ({ file, line, column })=>{
|
|
695
|
+
if (!file) return;
|
|
696
|
+
if (void 0 === line) return file;
|
|
697
|
+
return `${file}:${line}:${(column ?? 0) + 1}`;
|
|
698
|
+
};
|
|
699
|
+
const formatCodeFrame = ({ line, column, length, lineText })=>{
|
|
700
|
+
if (void 0 === line || void 0 === lineText) return;
|
|
701
|
+
const gutter = `${line} | `;
|
|
702
|
+
const caretIndent = ' '.repeat(gutter.length - 2) + '| ';
|
|
703
|
+
const caret = ' '.repeat(column ?? 0) + '^'.repeat(Math.max(1, length ?? 1));
|
|
704
|
+
return `${gutter}${lineText}\n${caretIndent}${caret}`;
|
|
705
|
+
};
|
|
706
|
+
const toAngularDiagnosticError = (message, severity)=>{
|
|
707
|
+
const sections = [
|
|
708
|
+
message.text ?? 'Unknown Angular compiler diagnostic'
|
|
709
|
+
];
|
|
710
|
+
const codeFrame = message.location && formatCodeFrame(message.location);
|
|
711
|
+
if (codeFrame) sections.push(codeFrame);
|
|
712
|
+
for (const note of message.notes ?? []){
|
|
713
|
+
const noteLocation = note.location && formatLocation(note.location);
|
|
714
|
+
sections.push([
|
|
715
|
+
noteLocation,
|
|
716
|
+
note.text
|
|
717
|
+
].filter((part)=>part).join(': '));
|
|
718
|
+
}
|
|
719
|
+
const error = new Error(sections.join('\n\n'));
|
|
720
|
+
error.name = 'error' === severity ? 'AngularCompilerError' : 'AngularCompilerWarning';
|
|
721
|
+
const file = message.location && formatLocation(message.location);
|
|
722
|
+
if (file) error.file = file;
|
|
723
|
+
error.hideStack = true;
|
|
724
|
+
return error;
|
|
725
|
+
};
|
|
726
|
+
const seedCompilationSourceFiles = (compilation, sourceFileCache)=>{
|
|
727
|
+
const { sourceFiles } = compilation;
|
|
728
|
+
if (!(sourceFiles instanceof Map)) return false;
|
|
729
|
+
for (const [fileName, sourceFile] of sourceFileCache)sourceFiles.set(fileName, sourceFile);
|
|
730
|
+
return true;
|
|
731
|
+
};
|
|
732
|
+
const MAIN_THREAD_DIRECTIVE = 'main thread';
|
|
733
|
+
const MAIN_THREAD_DIRECTIVE_ALT = 'main-thread';
|
|
734
|
+
const transformWorklets = (code, filename)=>{
|
|
735
|
+
if (!code.includes(MAIN_THREAD_DIRECTIVE) && !code.includes(MAIN_THREAD_DIRECTIVE_ALT)) return code;
|
|
736
|
+
const sourceFile = __rspack_external_typescript.createSourceFile(filename, code, __rspack_external_typescript.ScriptTarget.Latest, true);
|
|
737
|
+
const workletRanges = [];
|
|
738
|
+
visitNode(sourceFile, sourceFile, workletRanges);
|
|
739
|
+
if (0 === workletRanges.length) return code;
|
|
740
|
+
let result = code;
|
|
741
|
+
for(let i = workletRanges.length - 1; i >= 0; i--){
|
|
742
|
+
const { start, end } = workletRanges[i];
|
|
743
|
+
const fnText = result.slice(start, end);
|
|
744
|
+
result = result.slice(0, start) + `mainThreadFn(${fnText})` + result.slice(end);
|
|
745
|
+
}
|
|
746
|
+
result = `import { mainThreadFn } from '@blotch/angular-lynx';\n` + result;
|
|
747
|
+
return result;
|
|
748
|
+
};
|
|
749
|
+
const visitNode = (node, sourceFile, ranges)=>{
|
|
750
|
+
if (__rspack_external_typescript.isArrowFunction(node) || __rspack_external_typescript.isFunctionExpression(node)) {
|
|
751
|
+
if (hasMainThreadDirective(node) && !isAlreadyWrapped(node)) return void ranges.push({
|
|
752
|
+
start: node.getStart(sourceFile),
|
|
753
|
+
end: node.getEnd()
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
__rspack_external_typescript.forEachChild(node, (child)=>visitNode(child, sourceFile, ranges));
|
|
757
|
+
};
|
|
758
|
+
const hasMainThreadDirective = (node)=>{
|
|
759
|
+
const body = node.body;
|
|
760
|
+
if (!__rspack_external_typescript.isBlock(body)) return false;
|
|
761
|
+
if (0 === body.statements.length) return false;
|
|
762
|
+
const first = body.statements[0];
|
|
763
|
+
if (!__rspack_external_typescript.isExpressionStatement(first)) return false;
|
|
764
|
+
const expr = first.expression;
|
|
765
|
+
if (!__rspack_external_typescript.isStringLiteral(expr)) return false;
|
|
766
|
+
const text = expr.text;
|
|
767
|
+
return text === MAIN_THREAD_DIRECTIVE || text === MAIN_THREAD_DIRECTIVE_ALT;
|
|
768
|
+
};
|
|
769
|
+
const isAlreadyWrapped = (node)=>{
|
|
770
|
+
const parent = node.parent;
|
|
771
|
+
if (!parent || !__rspack_external_typescript.isCallExpression(parent)) return false;
|
|
772
|
+
const callee = parent.expression;
|
|
773
|
+
if (__rspack_external_typescript.isIdentifier(callee) && 'mainThreadFn' === callee.text) return true;
|
|
774
|
+
if (__rspack_external_typescript.isPropertyAccessExpression(callee) && 'mainThreadFn' === callee.name.text) return true;
|
|
775
|
+
return false;
|
|
776
|
+
};
|
|
777
|
+
const buildTransformedCode = (params)=>{
|
|
778
|
+
const { resourcePath, componentStyles, scopeInfo } = params;
|
|
779
|
+
let code = params.code;
|
|
780
|
+
if (componentStyles) {
|
|
781
|
+
const { imports } = componentStyles;
|
|
782
|
+
let importsString = '';
|
|
783
|
+
for(let i = 0; i < imports.length; ++i){
|
|
784
|
+
let relativeImport = node_path.relative(node_path.dirname(resourcePath), imports[i]);
|
|
785
|
+
if (!relativeImport.startsWith('.')) relativeImport = `./${relativeImport}`;
|
|
786
|
+
importsString += `import "${relativeImport}";`;
|
|
787
|
+
}
|
|
788
|
+
code = importsString + code;
|
|
789
|
+
}
|
|
790
|
+
if (scopeInfo) code += `\n;${scopeInfo.className}.ɵcmp.id = '${scopeInfo.scopeId}';\n`;
|
|
791
|
+
return transformWorklets(code, resourcePath);
|
|
792
|
+
};
|
|
793
|
+
const applyAngularConfig = (api, buildOptions)=>{
|
|
794
|
+
const __dirname = node_path.dirname(fileURLToPath(import.meta.url));
|
|
795
|
+
api.modifyRsbuildConfig((config)=>{
|
|
796
|
+
config.output = config.output ?? {};
|
|
797
|
+
if (buildOptions.outputPath) config.output.distPath = {
|
|
798
|
+
...'object' == typeof config.output.distPath ? config.output.distPath : {},
|
|
799
|
+
root: buildOptions.outputPath
|
|
800
|
+
};
|
|
801
|
+
config.output.cleanDistPath = true;
|
|
802
|
+
config.source ??= {};
|
|
803
|
+
if ('string' == typeof config.source.preEntry) config.source.preEntry = [
|
|
804
|
+
config.source.preEntry
|
|
805
|
+
];
|
|
806
|
+
else config.source.preEntry ??= [];
|
|
807
|
+
config.source.preEntry.push(node_path.resolve(__dirname, './polyfills'));
|
|
808
|
+
if (buildOptions.i18n.hasDefinedSourceLocale) config.source.preEntry.push('@angular/localize/init');
|
|
809
|
+
const polyfills = buildOptions.polyfills;
|
|
810
|
+
if (polyfills) config.source.preEntry.push(...polyfills);
|
|
811
|
+
config.source.preEntry.push(...buildOptions.styles);
|
|
812
|
+
config.source.tsconfigPath = buildOptions.tsconfig;
|
|
813
|
+
config.source.define ??= {};
|
|
814
|
+
config.source.define['__LYNX_SOURCE_LOCALE__'] = JSON.stringify(buildOptions.i18n.sourceLocale);
|
|
815
|
+
const isProd = 'production' === process.env.NODE_ENV || 'production' === config.mode;
|
|
816
|
+
if (isProd) config.source.define.ngDevMode = false;
|
|
817
|
+
});
|
|
818
|
+
};
|
|
819
|
+
const build_internals_require = createRequire(import.meta.url);
|
|
820
|
+
const angularBuildRoot = node_path.dirname(build_internals_require.resolve('@angular/build/package.json'));
|
|
821
|
+
const loadAngularBuildInternal = (subpath)=>build_internals_require(node_path.join(angularBuildRoot, subpath));
|
|
822
|
+
const { createAngularCompilation: createAngularCompilation, DiagnosticModes: DiagnosticModes } = loadAngularBuildInternal('src/tools/angular/compilation');
|
|
823
|
+
const { JavaScriptTransformer: JavaScriptTransformer } = loadAngularBuildInternal("src/tools/javascript-transformer");
|
|
824
|
+
const { ComponentStylesheetBundler: ComponentStylesheetBundler } = loadAngularBuildInternal('src/tools/esbuild/angular/component-stylesheets');
|
|
825
|
+
const isPresent = (variable)=>'string' == typeof variable && '' !== variable;
|
|
826
|
+
const isDisabled = (variable)=>'0' === variable || 'false' === variable.toLowerCase();
|
|
827
|
+
const maxWorkersVariable = process.env.NG_BUILD_MAX_WORKERS;
|
|
828
|
+
const maxWorkers = isPresent(maxWorkersVariable) ? +maxWorkersVariable : Math.min(4, Math.max(availableParallelism() - 1, 1));
|
|
829
|
+
const typeCheckingVariable = process.env.NG_BUILD_TYPE_CHECK;
|
|
272
830
|
const useTypeChecking = !isPresent(typeCheckingVariable) || !isDisabled(typeCheckingVariable);
|
|
831
|
+
/**
|
|
832
|
+
* @license
|
|
833
|
+
* Copyright Google LLC All Rights Reserved.
|
|
834
|
+
*
|
|
835
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
836
|
+
* found in the LICENSE file at https://angular.dev/license
|
|
837
|
+
*/ const findUp = (names, from)=>{
|
|
838
|
+
const namesList = Array.isArray(names) ? names : [
|
|
839
|
+
names
|
|
840
|
+
];
|
|
841
|
+
const root = __rspack_external_node_path_c5b9b54f.parse(from).root;
|
|
842
|
+
let currentDir = from;
|
|
843
|
+
while(currentDir && currentDir !== root){
|
|
844
|
+
for (const name of namesList){
|
|
845
|
+
const p = __rspack_external_node_path_c5b9b54f.join(currentDir, name);
|
|
846
|
+
if (existsSync(p)) return p;
|
|
847
|
+
}
|
|
848
|
+
currentDir = __rspack_external_node_path_c5b9b54f.dirname(currentDir);
|
|
849
|
+
}
|
|
850
|
+
return null;
|
|
851
|
+
};
|
|
852
|
+
const configNames = [
|
|
853
|
+
'angular.json',
|
|
854
|
+
'.angular.json'
|
|
855
|
+
];
|
|
856
|
+
const getAngularWorkspace = async (from = process.cwd())=>{
|
|
857
|
+
const host = workspaces.createWorkspaceHost(new NodeJsSyncHost());
|
|
858
|
+
const workspaceFile = findUp(configNames, from);
|
|
859
|
+
if (!workspaceFile) throw new Error("couldn't find workspace file");
|
|
860
|
+
const { workspace } = await workspaces.readWorkspace(workspaceFile, host);
|
|
861
|
+
return {
|
|
862
|
+
basePath: node_path.dirname(workspaceFile),
|
|
863
|
+
workspace
|
|
864
|
+
};
|
|
865
|
+
};
|
|
866
|
+
const findProjectByPath = (workspace, location, basePath)=>{
|
|
867
|
+
const isInside = (base, potential)=>{
|
|
868
|
+
const absoluteBase = node_path.resolve(basePath, base);
|
|
869
|
+
const absolutePotential = node_path.resolve(basePath, potential);
|
|
870
|
+
const relativePotential = node_path.relative(absoluteBase, absolutePotential);
|
|
871
|
+
if (!relativePotential.startsWith('..') && !node_path.isAbsolute(relativePotential)) return true;
|
|
872
|
+
return false;
|
|
873
|
+
};
|
|
874
|
+
const projects = Array.from(workspace.projects).map(([name, project])=>[
|
|
875
|
+
project.root,
|
|
876
|
+
name
|
|
877
|
+
]).filter((tuple)=>isInside(tuple[0], location)).sort((a, b)=>b[0].length - a[0].length);
|
|
878
|
+
if (0 === projects.length) return null;
|
|
879
|
+
if (projects.length > 1) {
|
|
880
|
+
const found = new Set();
|
|
881
|
+
const sameRoots = projects.filter((v)=>{
|
|
882
|
+
if (!found.has(v[0])) {
|
|
883
|
+
found.add(v[0]);
|
|
884
|
+
return false;
|
|
885
|
+
}
|
|
886
|
+
return true;
|
|
887
|
+
});
|
|
888
|
+
if (sameRoots.length > 0) return null;
|
|
889
|
+
}
|
|
890
|
+
return projects[0][1];
|
|
891
|
+
};
|
|
892
|
+
const getProjectByCwd = (workspace, basePath)=>{
|
|
893
|
+
if (1 === workspace.projects.size) return Array.from(workspace.projects.keys())[0];
|
|
894
|
+
const project = findProjectByPath(workspace, process.cwd(), basePath);
|
|
895
|
+
if (project) return project;
|
|
896
|
+
return null;
|
|
897
|
+
};
|
|
273
898
|
/**
|
|
274
899
|
* @license
|
|
275
900
|
* Copyright Google LLC All Rights Reserved.
|
|
@@ -338,29 +963,37 @@ const normalizeOptimization = (optimization = true)=>{
|
|
|
338
963
|
};
|
|
339
964
|
};
|
|
340
965
|
const readTarget = (project, target = 'build')=>project.targets.get(target);
|
|
341
|
-
const
|
|
966
|
+
const resolveIndexPath = (index, project, workspaceRoot)=>{
|
|
967
|
+
if ('boolean' == typeof index) return;
|
|
968
|
+
if ('string' == typeof index) return node_path.join(workspaceRoot, index);
|
|
969
|
+
if (index && 'object' == typeof index) return node_path.join(workspaceRoot, index.input);
|
|
970
|
+
const sourceRoot = project.sourceRoot ?? node_path.join(project.root ?? '', 'src');
|
|
971
|
+
return node_path.join(workspaceRoot, sourceRoot, 'index.html');
|
|
972
|
+
};
|
|
973
|
+
const resolveOutputPath = (outputPath, workspaceRoot)=>{
|
|
974
|
+
if ('string' == typeof outputPath) return node_path.join(workspaceRoot, outputPath);
|
|
975
|
+
if (outputPath && 'object' == typeof outputPath) return node_path.join(workspaceRoot, outputPath.base);
|
|
976
|
+
};
|
|
977
|
+
const readBuildTargetOptions = (project, configurationName)=>{
|
|
978
|
+
const target = readTarget(project);
|
|
979
|
+
if (!target) throw new Error("couldn't find target");
|
|
980
|
+
const resolvedConfigurationName = configurationName ?? target.defaultConfiguration;
|
|
981
|
+
const buildOptions = target.options;
|
|
982
|
+
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" and "tsConfig" defined.`);
|
|
983
|
+
return {
|
|
984
|
+
...buildOptions,
|
|
985
|
+
...resolvedConfigurationName ? target.configurations?.[resolvedConfigurationName] : void 0
|
|
986
|
+
};
|
|
987
|
+
};
|
|
988
|
+
const readBuildOptions = async (project, basePath, configurationName)=>normalizeBuildOptions(readBuildTargetOptions(project, configurationName), project, basePath);
|
|
989
|
+
const normalizeBuildOptions = async (buildOptions, project, basePath)=>{
|
|
342
990
|
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
991
|
const aot = buildOptions.aot ?? true;
|
|
359
992
|
const tsconfig = node_path.join(workspaceRoot, buildOptions.tsConfig);
|
|
360
993
|
const optimizationOptions = normalizeOptimization(buildOptions.optimization);
|
|
361
994
|
const sourcemapOptions = normalizeSourceMaps(buildOptions.sourceMap ?? false);
|
|
362
995
|
const browser = node_path.join(workspaceRoot, buildOptions.browser);
|
|
363
|
-
const index =
|
|
996
|
+
const index = resolveIndexPath(buildOptions.index, project, workspaceRoot);
|
|
364
997
|
let polyfills = buildOptions.polyfills;
|
|
365
998
|
polyfills = void 0 === polyfills || Array.isArray(polyfills) ? polyfills : [
|
|
366
999
|
polyfills
|
|
@@ -387,7 +1020,7 @@ const readBuildOptions = async (project, basePath, configurationName)=>{
|
|
|
387
1020
|
const cacheOptions = normalizeCacheOptions(buildOptions.projectMetadata, workspaceRoot);
|
|
388
1021
|
const inlineStyleLanguage = buildOptions.inlineStyleLanguage;
|
|
389
1022
|
const styles = (buildOptions.styles ?? []).map((stylePath)=>node_path.join(workspaceRoot, stylePath));
|
|
390
|
-
const outputPath =
|
|
1023
|
+
const outputPath = resolveOutputPath(buildOptions.outputPath, workspaceRoot);
|
|
391
1024
|
const i18n = normalizeI18nOptions(project.extensions['i18n']);
|
|
392
1025
|
const i18nMissingTranslation = buildOptions.i18nMissingTranslation ?? 'warning';
|
|
393
1026
|
const normalizedOptions = {
|
|
@@ -408,7 +1041,8 @@ const readBuildOptions = async (project, basePath, configurationName)=>{
|
|
|
408
1041
|
outputHashing,
|
|
409
1042
|
styles,
|
|
410
1043
|
i18n,
|
|
411
|
-
i18nMissingTranslation
|
|
1044
|
+
i18nMissingTranslation,
|
|
1045
|
+
budgets: buildOptions.budgets ?? []
|
|
412
1046
|
};
|
|
413
1047
|
return normalizedOptions;
|
|
414
1048
|
};
|
|
@@ -432,73 +1066,20 @@ const normalizeI18nOptions = (raw)=>{
|
|
|
432
1066
|
hasDefinedSourceLocale
|
|
433
1067
|
};
|
|
434
1068
|
};
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
const
|
|
443
|
-
|
|
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);
|
|
1069
|
+
const resolvePrimaryBuild = async ({ workspace, basePath, pages, cli })=>{
|
|
1070
|
+
let projectName;
|
|
1071
|
+
if (pages) {
|
|
1072
|
+
const candidates = 'all' === pages ? Array.from(workspace.projects.entries()).filter(([, project])=>'application' === project.extensions['projectType']).map(([name])=>name) : pages;
|
|
1073
|
+
projectName = cli && candidates.includes(cli.project) ? cli.project : candidates[0] ?? null;
|
|
1074
|
+
} else projectName = cli?.project ?? getProjectByCwd(workspace, basePath);
|
|
1075
|
+
if (!projectName) throw new Error("couldn't find the project");
|
|
1076
|
+
const project = workspace.projects.get(projectName);
|
|
1077
|
+
if (!project) throw new Error(`Project "${projectName}" not found in workspace`);
|
|
1078
|
+
const buildOptions = cli?.project === projectName ? await normalizeBuildOptions(cli.buildOptions, project, basePath) : await readBuildOptions(project, basePath);
|
|
466
1079
|
return {
|
|
467
|
-
|
|
468
|
-
|
|
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;
|
|
1080
|
+
projectName,
|
|
1081
|
+
buildOptions
|
|
478
1082
|
};
|
|
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
1083
|
};
|
|
503
1084
|
const resolvePages = (workspace, basePath, pages)=>{
|
|
504
1085
|
const projectNames = 'all' === pages ? Array.from(workspace.projects.entries()).filter(([, def])=>'application' === def.extensions['projectType']).map(([name])=>name) : pages;
|
|
@@ -618,668 +1199,393 @@ const collectElementTags = (code)=>{
|
|
|
618
1199
|
const tags = new Set();
|
|
619
1200
|
let match;
|
|
620
1201
|
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
|
|
1202
|
+
for (const chain of code.matchAll(chainPattern)){
|
|
1203
|
+
tagExtractor.lastIndex = 0;
|
|
1204
|
+
while(null !== (tagMatch = tagExtractor.exec(chain[0])))tags.add(tagMatch[1]);
|
|
1205
|
+
}
|
|
1206
|
+
while(null !== (match = repeaterPattern.exec(code)))tags.add(match[1]);
|
|
1207
|
+
return tags;
|
|
1208
|
+
};
|
|
1209
|
+
const scanCompiledOutputForHtmlElements = (fileCache)=>{
|
|
1210
|
+
const diagnostics = [];
|
|
1211
|
+
for (const [file, contents] of fileCache){
|
|
1212
|
+
if (file.includes('node_modules')) continue;
|
|
1213
|
+
const code = 'string' == typeof contents ? contents : Buffer.from(contents).toString();
|
|
1214
|
+
const tags = collectElementTags(code);
|
|
1215
|
+
const fileName = node_path.relative(process.cwd(), file);
|
|
1216
|
+
for (const tag of tags){
|
|
1217
|
+
if (!(tag in HTML_ELEMENT_SUGGESTIONS)) continue;
|
|
1218
|
+
const replacement = HTML_ELEMENT_SUGGESTIONS[tag];
|
|
1219
|
+
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.`;
|
|
1220
|
+
diagnostics.push({
|
|
1221
|
+
file,
|
|
1222
|
+
message,
|
|
1223
|
+
category: 'html-element'
|
|
815
1224
|
});
|
|
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
1225
|
}
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
1226
|
+
}
|
|
1227
|
+
return diagnostics;
|
|
1228
|
+
};
|
|
1229
|
+
const scanSourcesForUnsupportedPatterns = (fileNames)=>{
|
|
1230
|
+
const diagnostics = [];
|
|
1231
|
+
for (const filePath of fileNames){
|
|
1232
|
+
if (filePath.includes('node_modules')) continue;
|
|
1233
|
+
let source;
|
|
826
1234
|
try {
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
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?.();
|
|
1235
|
+
source = node_fs.readFileSync(filePath, 'utf-8');
|
|
1236
|
+
} catch {
|
|
1237
|
+
continue;
|
|
879
1238
|
}
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
const contents = await javascriptTransformer.transformData(context.resourcePath, context.code, {
|
|
887
|
-
skipLinker: false,
|
|
888
|
-
sideEffects: ()=>Promise.resolve(false)
|
|
1239
|
+
if (source.includes('ViewEncapsulation.ShadowDom')) {
|
|
1240
|
+
const fileName = node_path.relative(process.cwd(), filePath);
|
|
1241
|
+
diagnostics.push({
|
|
1242
|
+
file: filePath,
|
|
1243
|
+
message: `${fileName}: ViewEncapsulation.ShadowDom is not supported in Lynx (falls back to None). Use Emulated instead.`,
|
|
1244
|
+
category: 'structural'
|
|
889
1245
|
});
|
|
890
|
-
return {
|
|
891
|
-
code: transformWorklets(Buffer.from(contents).toString(), context.resourcePath)
|
|
892
|
-
};
|
|
893
1246
|
}
|
|
894
|
-
|
|
895
|
-
|
|
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'
|
|
1247
|
+
}
|
|
1248
|
+
return diagnostics;
|
|
911
1249
|
};
|
|
912
|
-
const
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
syntax: "typescript"
|
|
927
|
-
}
|
|
928
|
-
}
|
|
929
|
-
}).end();
|
|
930
|
-
});
|
|
1250
|
+
const scanCompiledOutputForStructuralIssues = (fileCache)=>{
|
|
1251
|
+
const diagnostics = [];
|
|
1252
|
+
for (const [file, contents] of fileCache){
|
|
1253
|
+
if (file.includes('node_modules')) continue;
|
|
1254
|
+
const code = 'string' == typeof contents ? contents : Buffer.from(contents).toString();
|
|
1255
|
+
const tags = collectElementTags(code);
|
|
1256
|
+
const fileName = node_path.relative(process.cwd(), file);
|
|
1257
|
+
if (tags.has('list-item') && !tags.has('list')) diagnostics.push({
|
|
1258
|
+
file,
|
|
1259
|
+
message: `${fileName}: <list-item> used without a <list> parent. Wrap list items in a <list> element.`,
|
|
1260
|
+
category: 'structural'
|
|
1261
|
+
});
|
|
1262
|
+
}
|
|
1263
|
+
return diagnostics;
|
|
931
1264
|
};
|
|
932
|
-
const
|
|
933
|
-
const
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
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();
|
|
1265
|
+
const scanSourcesForUnsupportedCss = (fileNames)=>{
|
|
1266
|
+
const diagnostics = [];
|
|
1267
|
+
for (const filePath of fileNames){
|
|
1268
|
+
if (filePath.includes('node_modules')) continue;
|
|
1269
|
+
let source;
|
|
1270
|
+
try {
|
|
1271
|
+
source = node_fs.readFileSync(filePath, 'utf-8');
|
|
1272
|
+
} catch {
|
|
1273
|
+
continue;
|
|
968
1274
|
}
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
1275
|
+
if (!source.includes('@Component')) continue;
|
|
1276
|
+
const cssChunks = [];
|
|
1277
|
+
const stylesBlockMatch = source.match(/styles\s*:\s*\[([\s\S]*?)\]/);
|
|
1278
|
+
if (stylesBlockMatch) {
|
|
1279
|
+
for (const m of stylesBlockMatch[1].matchAll(/`([\s\S]*?)`/g))cssChunks.push(m[1]);
|
|
1280
|
+
if (0 === cssChunks.length) for (const m of stylesBlockMatch[1].matchAll(/(['"])([\s\S]*?)\1/g))cssChunks.push(m[2]);
|
|
972
1281
|
}
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
1282
|
+
const styleUrlsMatch = source.match(/styleUrls?\s*:\s*\[([\s\S]*?)\]/);
|
|
1283
|
+
if (styleUrlsMatch) for (const m of styleUrlsMatch[1].matchAll(/['"]([^'"]+)['"]/g)){
|
|
1284
|
+
const stylePath = node_path.resolve(node_path.dirname(filePath), m[1]);
|
|
1285
|
+
try {
|
|
1286
|
+
cssChunks.push(node_fs.readFileSync(stylePath, 'utf-8'));
|
|
1287
|
+
} catch {}
|
|
977
1288
|
}
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
});
|
|
1289
|
+
const seen = new Set();
|
|
1290
|
+
const fileName = node_path.relative(process.cwd(), filePath);
|
|
1291
|
+
for (const css of cssChunks){
|
|
1292
|
+
const withoutComments = css.replace(/\/\*[\s\S]*?\*\//g, '').replace(/\/\/.*/g, '');
|
|
1293
|
+
const propertyPattern = /(?:^|[{;])\s*([a-z-]+)\s*:/gm;
|
|
1294
|
+
let match;
|
|
1295
|
+
while(null !== (match = propertyPattern.exec(withoutComments))){
|
|
1296
|
+
const property = match[1];
|
|
1297
|
+
if (UNSUPPORTED_CSS[property] && !seen.has(property)) {
|
|
1298
|
+
seen.add(property);
|
|
1299
|
+
diagnostics.push({
|
|
1300
|
+
file: filePath,
|
|
1301
|
+
message: `${fileName}: '${property}' — ${UNSUPPORTED_CSS[property]}.`,
|
|
1302
|
+
category: 'css'
|
|
1303
|
+
});
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
return diagnostics;
|
|
999
1309
|
};
|
|
1000
|
-
const
|
|
1001
|
-
if (
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
};
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
};
|
|
1310
|
+
const reportLynxDiagnostics = (diagnostics)=>{
|
|
1311
|
+
if (0 === diagnostics.length) return;
|
|
1312
|
+
const htmlWarnings = diagnostics.filter((d)=>'html-element' === d.category);
|
|
1313
|
+
const structuralWarnings = diagnostics.filter((d)=>'structural' === d.category);
|
|
1314
|
+
const cssWarnings = diagnostics.filter((d)=>'css' === d.category);
|
|
1315
|
+
if (htmlWarnings.length > 0) {
|
|
1316
|
+
console.warn('[Lynx] HTML elements detected in templates:');
|
|
1317
|
+
for (const w of htmlWarnings)console.warn(` ${w.message}`);
|
|
1318
|
+
}
|
|
1319
|
+
if (structuralWarnings.length > 0) {
|
|
1320
|
+
console.warn('[Lynx] Structural issues:');
|
|
1321
|
+
for (const w of structuralWarnings)console.warn(` ${w.message}`);
|
|
1322
|
+
}
|
|
1323
|
+
if (cssWarnings.length > 0) {
|
|
1324
|
+
console.warn('[Lynx] Unsupported CSS properties:');
|
|
1325
|
+
for (const w of cssWarnings)console.warn(` ${w.message}`);
|
|
1016
1326
|
}
|
|
1017
|
-
return options;
|
|
1018
1327
|
};
|
|
1019
|
-
const
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
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
|
-
}
|
|
1328
|
+
const applyAngularRules = async (api, pluginOptions)=>{
|
|
1329
|
+
const cli = getAngularCliContext(api);
|
|
1330
|
+
const { basePath, workspace } = await getAngularWorkspace(cli?.workspaceRoot);
|
|
1331
|
+
if (pluginOptions.pages) {
|
|
1332
|
+
const pages = resolvePages(workspace, basePath, pluginOptions.pages);
|
|
1333
|
+
api.modifyRsbuildConfig((config)=>{
|
|
1334
|
+
config.source ??= {};
|
|
1335
|
+
config.source.entry = Object.fromEntries(pages.map((page)=>[
|
|
1336
|
+
page.name,
|
|
1337
|
+
page.browser
|
|
1338
|
+
]));
|
|
1339
|
+
});
|
|
1046
1340
|
}
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1341
|
+
const { buildOptions } = await resolvePrimaryBuild({
|
|
1342
|
+
workspace,
|
|
1343
|
+
basePath,
|
|
1344
|
+
pages: pluginOptions.pages,
|
|
1345
|
+
cli
|
|
1346
|
+
});
|
|
1347
|
+
applyAngularConfig(api, buildOptions);
|
|
1348
|
+
const componentStyleSizes = new Map();
|
|
1349
|
+
applyBudgets(api, {
|
|
1350
|
+
budgets: buildOptions.budgets,
|
|
1351
|
+
componentStyleSizes,
|
|
1352
|
+
underAngularCli: void 0 !== cli
|
|
1353
|
+
});
|
|
1354
|
+
const sourcemap = !!(!!buildOptions.sourcemapOptions.scripts && (buildOptions.sourcemapOptions.hidden ? 'external' : true));
|
|
1355
|
+
const thirdPartySourcemaps = buildOptions.sourcemapOptions.vendor;
|
|
1356
|
+
const advancedOptimizations = buildOptions.advancedOptimizations;
|
|
1357
|
+
const aot = buildOptions.aot;
|
|
1358
|
+
const javascriptTransformer = new JavaScriptTransformer({
|
|
1359
|
+
sourcemap,
|
|
1360
|
+
thirdPartySourcemaps,
|
|
1361
|
+
advancedOptimizations,
|
|
1362
|
+
jit: !aot,
|
|
1363
|
+
maxConcurrency: maxWorkers
|
|
1364
|
+
}, void 0);
|
|
1365
|
+
const tsconfig = buildOptions.tsconfig;
|
|
1366
|
+
const compilation = await createAngularCompilation(false, aot);
|
|
1367
|
+
const typeScriptFileCache = new Map();
|
|
1368
|
+
const scopedCssCacheDir = node_path.join(basePath, 'node_modules', '.cache', 'angular-lynx-css');
|
|
1369
|
+
node_fs.mkdirSync(scopedCssCacheDir, {
|
|
1370
|
+
recursive: true
|
|
1371
|
+
});
|
|
1372
|
+
const componentStylesCache = new Map();
|
|
1373
|
+
const componentScopeIds = new Map();
|
|
1374
|
+
let componentResourceDependencies = new Map();
|
|
1375
|
+
const writtenStylesheets = new Set();
|
|
1376
|
+
let knownSourceFiles = new Set();
|
|
1377
|
+
let angularDiagnostics = {
|
|
1378
|
+
errors: [],
|
|
1379
|
+
warnings: []
|
|
1053
1380
|
};
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
firstScreenSyncTiming: 'immediately',
|
|
1062
|
-
enableSSR: false,
|
|
1063
|
-
isWeb: false,
|
|
1064
|
-
mainThreadChunks: [],
|
|
1065
|
-
extractStr: false,
|
|
1066
|
-
experimental_isLazyBundle: false
|
|
1381
|
+
const transformStylesheet = createTransformStylesheet({
|
|
1382
|
+
basePath,
|
|
1383
|
+
scopedCssCacheDir,
|
|
1384
|
+
componentStylesCache,
|
|
1385
|
+
componentScopeIds,
|
|
1386
|
+
onStylesheetWritten: (filePath)=>writtenStylesheets.add(filePath),
|
|
1387
|
+
componentStyleSizes
|
|
1067
1388
|
});
|
|
1068
|
-
|
|
1069
|
-
const
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
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
|
-
}
|
|
1389
|
+
const compileAngular = async (changedFiles)=>{
|
|
1390
|
+
const { sourceFileCache, fileNames } = buildLynxSchemaSourceFileCache(tsconfig, changedFiles && ((file)=>changedFiles.has(node_path.normalize(file)) || !knownSourceFiles.has(file)));
|
|
1391
|
+
knownSourceFiles = new Set(fileNames);
|
|
1392
|
+
if (changedFiles) await compilation.update?.(new Set(changedFiles));
|
|
1393
|
+
const seeded = seedCompilationSourceFiles(compilation, sourceFileCache);
|
|
1394
|
+
const seededFiles = new Set(seeded ? [
|
|
1395
|
+
...sourceFileCache.keys()
|
|
1396
|
+
].map((file)=>node_path.normalize(file)) : []);
|
|
1397
|
+
writtenStylesheets.clear();
|
|
1398
|
+
let result;
|
|
1399
|
+
try {
|
|
1400
|
+
result = await compilation.initialize(tsconfig, {
|
|
1401
|
+
processWebWorker: (workerFile, _containingFile)=>workerFile,
|
|
1402
|
+
transformStylesheet,
|
|
1403
|
+
modifiedFiles: changedFiles && new Set([
|
|
1404
|
+
...changedFiles
|
|
1405
|
+
].filter((file)=>!seededFiles.has(file)))
|
|
1406
|
+
}, {
|
|
1407
|
+
sourcemap: !!sourcemap,
|
|
1408
|
+
preserveSymlinks: false
|
|
1104
1409
|
});
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
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
|
-
});
|
|
1410
|
+
} catch (error) {
|
|
1411
|
+
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)}`);
|
|
1412
|
+
}
|
|
1413
|
+
componentResourceDependencies = new Map([
|
|
1414
|
+
...result.componentResourcesDependencies ?? []
|
|
1415
|
+
].map(([file, dependencies])=>[
|
|
1416
|
+
node_path.normalize(file),
|
|
1417
|
+
dependencies.map((dependency)=>node_path.normalize(dependency))
|
|
1418
|
+
]));
|
|
1419
|
+
const changedOutputs = new Set();
|
|
1420
|
+
try {
|
|
1421
|
+
for (const { filename, contents } of (await compilation.emitAffectedFiles())){
|
|
1422
|
+
const file = node_path.normalize(filename);
|
|
1423
|
+
if (typeScriptFileCache.get(file) !== contents) changedOutputs.add(file);
|
|
1424
|
+
typeScriptFileCache.set(file, contents);
|
|
1125
1425
|
}
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
(
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1426
|
+
} catch (error) {
|
|
1427
|
+
throw new Error(`Angular failed to emit compiled output for "${tsconfig}". Original error: ${error instanceof Error ? error.stack ?? error.message : String(error)}`);
|
|
1428
|
+
}
|
|
1429
|
+
for (const file of writtenStylesheets)changedOutputs.add(file);
|
|
1430
|
+
for (const file of changedFiles ?? [])if (!node_fs.existsSync(file)) {
|
|
1431
|
+
typeScriptFileCache.delete(file);
|
|
1432
|
+
forgetComponentStyleSizes(componentStyleSizes, file);
|
|
1433
|
+
}
|
|
1434
|
+
reportLynxDiagnostics([
|
|
1435
|
+
...scanCompiledOutputForHtmlElements(typeScriptFileCache),
|
|
1436
|
+
...scanCompiledOutputForStructuralIssues(typeScriptFileCache),
|
|
1437
|
+
...scanSourcesForUnsupportedPatterns(fileNames),
|
|
1438
|
+
...scanSourcesForUnsupportedCss(fileNames)
|
|
1439
|
+
]);
|
|
1440
|
+
angularDiagnostics = filterAngularDiagnostics(await compilation.diagnoseFiles(useTypeChecking ? DiagnosticModes.All : DiagnosticModes.All & ~DiagnosticModes.Semantic));
|
|
1441
|
+
return {
|
|
1442
|
+
changedOutputs,
|
|
1443
|
+
inputFiles: result.referencedFiles.map((file)=>node_path.normalize(file))
|
|
1444
|
+
};
|
|
1445
|
+
};
|
|
1446
|
+
const coordinator = createAngularRebuildCoordinator({
|
|
1447
|
+
compile: compileAngular
|
|
1448
|
+
});
|
|
1449
|
+
const compilersByEnvironment = new Map();
|
|
1450
|
+
api.onAfterCreateCompiler(({ compiler })=>{
|
|
1451
|
+
const compilers = 'compilers' in compiler ? compiler.compilers : [
|
|
1452
|
+
compiler
|
|
1453
|
+
];
|
|
1454
|
+
for (const environmentCompiler of compilers)if (environmentCompiler.name) compilersByEnvironment.set(environmentCompiler.name, environmentCompiler);
|
|
1455
|
+
compilers[0]?.hooks.thisCompilation.tap('lynx:angular:diagnostics', (rspackCompilation)=>{
|
|
1456
|
+
rspackCompilation.errors.push(...angularDiagnostics.errors.map((message)=>toAngularDiagnosticError(message, 'error')));
|
|
1457
|
+
rspackCompilation.warnings.push(...angularDiagnostics.warnings.map((message)=>toAngularDiagnosticError(message, 'warning')));
|
|
1153
1458
|
});
|
|
1154
|
-
}
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
const
|
|
1158
|
-
const
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1459
|
+
});
|
|
1460
|
+
let compilationClosed = false;
|
|
1461
|
+
api.onBeforeEnvironmentCompile(async ({ environment, isWatch })=>{
|
|
1462
|
+
const compiler = compilersByEnvironment.get(environment.name);
|
|
1463
|
+
const outdatedFiles = await coordinator.prepare(environment.name, [
|
|
1464
|
+
...compiler?.modifiedFiles ?? [],
|
|
1465
|
+
...compiler?.removedFiles ?? []
|
|
1466
|
+
]);
|
|
1467
|
+
if (compiler && outdatedFiles.size > 0) compiler.modifiedFiles = new Set([
|
|
1468
|
+
...compiler.modifiedFiles ?? [],
|
|
1469
|
+
...outdatedFiles
|
|
1470
|
+
]);
|
|
1471
|
+
if (!isWatch && !compilationClosed) {
|
|
1472
|
+
compilationClosed = true;
|
|
1473
|
+
await compilation.close?.();
|
|
1474
|
+
}
|
|
1475
|
+
});
|
|
1476
|
+
api.transform({
|
|
1477
|
+
test: /\.[cm]?[jt]sx?$/
|
|
1478
|
+
}, async (context)=>{
|
|
1479
|
+
const isJs = /\.[cm]?js$/.test(context.resourcePath);
|
|
1480
|
+
if (isJs) {
|
|
1481
|
+
const contents = await javascriptTransformer.transformData(context.resourcePath, context.code, {
|
|
1482
|
+
skipLinker: false,
|
|
1483
|
+
sideEffects: ()=>Promise.resolve(false)
|
|
1178
1484
|
});
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
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();
|
|
1485
|
+
return {
|
|
1486
|
+
code: transformWorklets(Buffer.from(contents).toString(), context.resourcePath)
|
|
1487
|
+
};
|
|
1231
1488
|
}
|
|
1232
|
-
|
|
1233
|
-
|
|
1489
|
+
const content = typeScriptFileCache.get(context.resourcePath);
|
|
1490
|
+
if (!content) throw new Error(`No compiled output found for ${context.resourcePath}`);
|
|
1491
|
+
for (const dependency of componentResourceDependencies.get(context.resourcePath) ?? [])context.addDependency(dependency);
|
|
1492
|
+
const code = 'string' == typeof content ? content : Buffer.from(content).toString();
|
|
1493
|
+
return {
|
|
1494
|
+
code: buildTransformedCode({
|
|
1495
|
+
code,
|
|
1496
|
+
resourcePath: context.resourcePath,
|
|
1497
|
+
componentStyles: componentStylesCache.get(context.resourcePath),
|
|
1498
|
+
scopeInfo: componentScopeIds.get(context.resourcePath)
|
|
1499
|
+
})
|
|
1500
|
+
};
|
|
1501
|
+
});
|
|
1502
|
+
};
|
|
1503
|
+
const applyCSS = (api, options)=>{
|
|
1504
|
+
const { enableRemoveCSSScope, enableCSSSelector, enableCSSInvalidation, targetSdkVersion } = options;
|
|
1505
|
+
api.modifyRsbuildConfig((config, { mergeRsbuildConfig })=>mergeRsbuildConfig(config, {
|
|
1506
|
+
output: {
|
|
1507
|
+
injectStyles: false
|
|
1508
|
+
}
|
|
1509
|
+
}));
|
|
1510
|
+
const __dirname = node_path.dirname(fileURLToPath(import.meta.url));
|
|
1511
|
+
api.modifyBundlerChain(async (chain, { CHAIN_ID, environment })=>{
|
|
1512
|
+
const { CssExtractRspackPlugin } = await import("@lynx-js/css-extract-webpack-plugin");
|
|
1513
|
+
const removeLightningCSS = (rule)=>{
|
|
1514
|
+
if (rule.uses.has(CHAIN_ID.USE.LIGHTNINGCSS) && 'lynx' === environment.name) rule.uses.delete(CHAIN_ID.USE.LIGHTNINGCSS);
|
|
1515
|
+
};
|
|
1516
|
+
const cssRules = [
|
|
1517
|
+
CHAIN_ID.RULE.CSS,
|
|
1518
|
+
CHAIN_ID.RULE.SASS,
|
|
1519
|
+
CHAIN_ID.RULE.LESS,
|
|
1520
|
+
CHAIN_ID.RULE.STYLUS
|
|
1521
|
+
];
|
|
1522
|
+
for (const ruleName of cssRules.filter((rule)=>chain.module.rules.has(rule))){
|
|
1523
|
+
const rule = chain.module.rule(ruleName);
|
|
1524
|
+
const mainRuleName = ruleName === CHAIN_ID.RULE.CSS ? CHAIN_ID.ONE_OF.CSS_MAIN : ruleName;
|
|
1525
|
+
const mainRule = rule.oneOf(mainRuleName);
|
|
1526
|
+
const parentRuleEntries = rule.entries();
|
|
1527
|
+
removeLightningCSS(mainRule);
|
|
1528
|
+
mainRule.issuerLayer(LAYERS.BACKGROUND).use(CHAIN_ID.USE.MINI_CSS_EXTRACT).loader(CssExtractRspackPlugin.loader).end();
|
|
1529
|
+
const uses = mainRule.uses.entries() ?? {};
|
|
1530
|
+
const ruleEntries = mainRule.entries();
|
|
1531
|
+
const cssLoader = uses[CHAIN_ID.USE.CSS];
|
|
1532
|
+
if (!cssLoader) continue;
|
|
1533
|
+
const cssLoaderRule = cssLoader.entries();
|
|
1534
|
+
const mainThreadLayerRule = chain.module.rule(`${ruleName}:${LAYERS.MAIN_THREAD}`).test(parentRuleEntries.test).merge(ruleEntries).issuerLayer(LAYERS.MAIN_THREAD);
|
|
1535
|
+
if (void 0 !== parentRuleEntries.dependency) mainThreadLayerRule.merge({
|
|
1536
|
+
dependency: parentRuleEntries.dependency
|
|
1537
|
+
});
|
|
1538
|
+
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();
|
|
1539
|
+
}
|
|
1540
|
+
for (const ruleName of cssRules.filter((rule)=>chain.module.rules.has(rule))){
|
|
1541
|
+
const inlineRuleName = ruleName === CHAIN_ID.RULE.CSS ? CHAIN_ID.ONE_OF.CSS_INLINE : `${ruleName}-inline`;
|
|
1542
|
+
removeLightningCSS(chain.module.rule(ruleName).oneOf(inlineRuleName));
|
|
1543
|
+
}
|
|
1544
|
+
if ('lynx' === environment.name && chain.module.rules.has(CHAIN_ID.RULE.FONT)) {
|
|
1545
|
+
const fontRule = chain.module.rule(CHAIN_ID.RULE.FONT);
|
|
1546
|
+
fontRule.oneOfs.clear();
|
|
1547
|
+
fontRule.type('asset/inline');
|
|
1548
|
+
}
|
|
1549
|
+
chain.plugin(CHAIN_ID.PLUGIN.MINI_CSS_EXTRACT).tap(([options])=>[
|
|
1234
1550
|
{
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
});
|
|
1240
|
-
},
|
|
1551
|
+
...options,
|
|
1552
|
+
enableRemoveCSSScope,
|
|
1553
|
+
enableCSSSelector,
|
|
1554
|
+
enableCSSInvalidation,
|
|
1241
1555
|
targetSdkVersion,
|
|
1242
|
-
|
|
1556
|
+
cssPlugins: [
|
|
1557
|
+
CSSPlugins.parserPlugins.removeFunctionWhiteSpace()
|
|
1558
|
+
]
|
|
1243
1559
|
}
|
|
1244
|
-
]).end();
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
enableSSR: options.enableSSR,
|
|
1254
|
-
isWeb
|
|
1255
|
-
}
|
|
1256
|
-
]);
|
|
1560
|
+
]).init((_, args)=>new CssExtractRspackPlugin(...args)).end().end();
|
|
1561
|
+
chain.module.when(void 0 === enableRemoveCSSScope, (module)=>module.rule('lynx.css.scoped').test(/\.css$/).resourceQuery({
|
|
1562
|
+
and: [
|
|
1563
|
+
/cssId/,
|
|
1564
|
+
{
|
|
1565
|
+
not: /common/
|
|
1566
|
+
}
|
|
1567
|
+
]
|
|
1568
|
+
}).sideEffects(false));
|
|
1257
1569
|
});
|
|
1258
1570
|
};
|
|
1259
|
-
const
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
else imports.push(item.import);
|
|
1276
|
-
if (dependOn) if ('string' == typeof dependOn) chunks.unshift(dependOn);
|
|
1277
|
-
else chunks.unshift(...dependOn);
|
|
1571
|
+
const normalizeCssLoaderOptions = (options, exportOnlyLocals)=>{
|
|
1572
|
+
if (options.modules && exportOnlyLocals) {
|
|
1573
|
+
let { modules } = options;
|
|
1574
|
+
modules = true === modules ? {
|
|
1575
|
+
exportOnlyLocals: true
|
|
1576
|
+
} : 'string' == typeof modules ? {
|
|
1577
|
+
mode: modules,
|
|
1578
|
+
exportOnlyLocals: true
|
|
1579
|
+
} : {
|
|
1580
|
+
...modules,
|
|
1581
|
+
exportOnlyLocals: true
|
|
1582
|
+
};
|
|
1583
|
+
return {
|
|
1584
|
+
...options,
|
|
1585
|
+
modules
|
|
1586
|
+
};
|
|
1278
1587
|
}
|
|
1279
|
-
return
|
|
1280
|
-
chunks,
|
|
1281
|
-
imports
|
|
1282
|
-
};
|
|
1588
|
+
return options;
|
|
1283
1589
|
};
|
|
1284
1590
|
const applyGenerator = (api)=>{
|
|
1285
1591
|
api.modifyBundlerChain({
|
|
@@ -1432,8 +1738,7 @@ const applySplitChunksRule = (api, options)=>{
|
|
|
1432
1738
|
});
|
|
1433
1739
|
return config;
|
|
1434
1740
|
});
|
|
1435
|
-
api.modifyRspackConfig((rspackConfig
|
|
1436
|
-
if ('lynx' !== environment.name) return rspackConfig;
|
|
1741
|
+
api.modifyRspackConfig((rspackConfig)=>{
|
|
1437
1742
|
rspackConfig.output = rspackConfig.output ?? {};
|
|
1438
1743
|
if (!options.experimental_isLazyBundle) rspackConfig.output.asyncChunks = false;
|
|
1439
1744
|
if (!rspackConfig.optimization) return rspackConfig;
|
|
@@ -1492,7 +1797,8 @@ const normalizeOptions = (options)=>{
|
|
|
1492
1797
|
const pluginAngularLynx = (options)=>({
|
|
1493
1798
|
name: 'lynx:angular',
|
|
1494
1799
|
pre: [
|
|
1495
|
-
'lynx:rsbuild:plugin-api'
|
|
1800
|
+
'lynx:rsbuild:plugin-api',
|
|
1801
|
+
ANGULAR_CLI_CONTEXT_PLUGIN_NAME
|
|
1496
1802
|
],
|
|
1497
1803
|
setup: async (api)=>{
|
|
1498
1804
|
const normalizedOptions = normalizeOptions(options);
|
|
@@ -1507,4 +1813,4 @@ const pluginAngularLynx = (options)=>({
|
|
|
1507
1813
|
await angularRules;
|
|
1508
1814
|
}
|
|
1509
1815
|
});
|
|
1510
|
-
export { pluginAngularLynx };
|
|
1816
|
+
export { pluginAngularCliContext, pluginAngularLynx };
|