@akanjs/cli 3.0.0-alpha.4 → 3.0.0-alpha.40
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/.build-stamp +1 -1
- package/README.ko.md +1 -1
- package/README.md +1 -1
- package/{abstractCompactor-rk6twzvj.js → abstractCompactor-20e0cwbs.js} +3 -3
- package/{agent.command-jfz8yfwy.js → agent.command-e835fjem.js} +9 -8
- package/{application.command-mxyqtd6p.js → application.command-sjtbk6tr.js} +7 -6
- package/{applicationBuildRunner-cwk52sb7.js → applicationBuildRunner-7gnfz7ww.js} +7 -8
- package/{applicationReleasePackager-yrrhbkv3.js → applicationReleasePackager-93nzdfsy.js} +2 -2
- package/buildBatch.proc.js +6 -5
- package/{capacitorApp-j357ak1g.js → capacitorApp-qv7g3dp2.js} +29 -28
- package/{cloud.command-f87dwe52.js → cloud.command-y0e4eyqz.js} +11 -10
- package/{context.command-z5bf8bn7.js → context.command-vn87jb08.js} +17 -16
- package/{dependencyScanner-f6grrep3.js → dependencyScanner-rbs2407b.js} +1 -1
- package/getDirname-ka11hm7e.js +8 -0
- package/{guideline.command-1m1ctar3.js → guideline.command-s4hn2bsy.js} +7 -6
- package/guidelines/conventions/conventions.generate.json +25 -0
- package/guidelines/conventions/conventions.instruction.md +1173 -0
- package/guidelines/cssRule/cssRule.instruction.md +7 -0
- package/guidelines/framework/framework.instruction.md +1 -0
- package/guidelines/ssrRule/ssrRule.generate.json +31 -0
- package/guidelines/ssrRule/ssrRule.instruction.md +108 -0
- package/guidelines/workspaceOnboarding/workspaceOnboarding.generate.json +24 -0
- package/guidelines/workspaceOnboarding/workspaceOnboarding.instruction.md +972 -0
- package/incrementalBuilder.proc.js +7 -6
- package/{index-1s71fwrq.js → index-0h212yqe.js} +13 -13
- package/index-1577bej2.js +7 -0
- package/{index-e3qskyt2.js → index-33zce8gt.js} +8 -4
- package/{index-csqk9z5j.js → index-3dp7x6rc.js} +3 -3
- package/{index-cn24dcqn.js → index-73rnbwgg.js} +15 -7
- package/{index-tq1y56tp.js → index-8d2sv6pw.js} +39 -16
- package/{index-jtm86byd.js → index-8vkkm7v0.js} +125 -66
- package/{index-r4w8cry5.js → index-9jsnb4vw.js} +4 -4
- package/{index-ybybkec0.js → index-becx1t0k.js} +6 -6
- package/{index-4yp06pn1.js → index-g9e2fhw2.js} +49 -32
- package/{index-cap2k2ch.js → index-hg0qvhmx.js} +152 -241
- package/{index-xwgm94zr.js → index-j37qq1f2.js} +1 -1
- package/{index-n0z3f2xg.js → index-k6fvbcj1.js} +37 -6
- package/{index-6pz1j0zj.js → index-mxvakhsm.js} +6 -2
- package/{index-vhv3xnjz.js → index-mykmf6wd.js} +8 -8
- package/{index-y0k42k04.js → index-mz5jtaq7.js} +1 -1
- package/{index-jk9q6pse.js → index-n706txcv.js} +5 -3
- package/{index-hjzr8ja1.js → index-qc0tnhs2.js} +1 -1
- package/{index-rh16j3c7.js → index-qnv0aqmm.js} +12 -8
- package/{index-3pgtw35d.js → index-sh01r3pm.js} +2 -2
- package/{index-xj8bks3p.js → index-t71zvmt9.js} +9 -11
- package/{index-4zgs8g1w.js → index-tsmjxph2.js} +1 -1
- package/{index-fmgbwsr5.js → index-x33gnap3.js} +4 -4
- package/{index-b978vs2e.js → index-xyrb7pk5.js} +3 -3
- package/index.js +21 -20
- package/{library.command-pwfx8dx2.js → library.command-v5cyaw9t.js} +6 -5
- package/{localRegistry.command-r4zjx5py.js → localRegistry.command-53t758pt.js} +10 -9
- package/{module.command-0t3ewata.js → module.command-pq6w84xg.js} +10 -9
- package/{package.command-7hmkxvpa.js → package.command-qkcs60ts.js} +6 -5
- package/package.json +3 -4
- package/{page.command-st6r4s0q.js → page.command-qj5bfnh9.js} +6 -5
- package/{primitive.command-1qkxpzsn.js → primitive.command-bw0nes6x.js} +11 -10
- package/{quality.command-hy5vt1bh.js → quality.command-g1s2wt8b.js} +532 -87
- package/{recipeScanner-tghkzrrt.js → recipeScanner-zrzev25x.js} +3 -3
- package/{repair.command-nakkq2zp.js → repair.command-mzwtejpk.js} +8 -7
- package/{routeSourceValidator-cncd92fv.js → routeSourceValidator-7fycdqyv.js} +3 -39
- package/{scalar.command-fjq272gs.js → scalar.command-rm6k3c65.js} +9 -8
- package/templates/app/env/env.server.debug.ts.template +1 -5
- package/templates/app/env/env.server.develop.ts.template +1 -5
- package/templates/app/env/env.server.local.ts.template +1 -5
- package/templates/app/env/env.server.main.ts.template +1 -5
- package/templates/app/page/styles.css.template +3 -3
- package/templates/appSample/srvkit/AuthGuard.ts +9 -5
- package/templates/appSample/srvkit/SessionInternalArg.ts +4 -3
- package/templates/crudPages/[__model__Id]/edit/page.tsx +1 -1
- package/templates/crudPages/new/page.tsx +1 -1
- package/templates/crudSinglePage/page.tsx +1 -1
- package/templates/env/_env.server.type.ts +7 -5
- package/templates/lib/db.ts +4 -2
- package/templates/lib/dict.ts +1 -1
- package/templates/lib/sig.ts +1 -1
- package/templates/lib/srv.ts +6 -2
- package/templates/lib/useClient.ts +9 -3
- package/templates/module/__Model__.Util.tsx +1 -1
- package/templates/module/__Model__.Zone.tsx +3 -5
- package/templates/workspaceRoot/AGENTS.md.template +0 -831
- package/templates/workspaceRoot/CLAUDE.md.template +19 -0
- package/templates/workspaceRoot/biome.json.template +4 -217
- package/{typeChecker-kravn7ns.js → typeChecker-g6bh0xfv.js} +1 -1
- package/typecheck.proc.js +1 -1
- package/{workflow.command-jswbgk90.js → workflow.command-g2cgcgxn.js} +16 -15
- package/{workspace.command-h9wwcjpt.js → workspace.command-f7jerqb2.js} +70 -52
- package/templates/libRoot/base/baseLogic.ts +0 -14
- package/templates/libRoot/base/index.ts +0 -8
- package/templates/workspaceRoot/.cursor/rules/akan-page-routing.mdc.template +0 -43
- package/templates/workspaceRoot/.cursor/rules/akan-sync-conventions.mdc.template +0 -33
- package/templates/workspaceRoot/.cursor/rules/application-test-commands.mdc.template +0 -16
- package/templates/workspaceRoot/.cursor/rules/change-scope.mdc.template +0 -12
- package/templates/workspaceRoot/.cursor/rules/client-server-boundaries.mdc.template +0 -17
- package/templates/workspaceRoot/.cursor/rules/coding-comments.mdc.template +0 -25
- package/templates/workspaceRoot/.cursor/rules/coding-style.mdc.template +0 -44
- package/templates/workspaceRoot/.cursor/rules/domain-module-conventions.mdc.template +0 -17
- package/templates/workspaceRoot/.cursor/rules/format-lint.mdc.template +0 -12
- package/templates/workspaceRoot/.cursor/rules/lint-enforced-rules.mdc.template +0 -18
- package/templates/workspaceRoot/.cursor/rules/module-file-playbook.mdc.template +0 -61
- package/templates/workspaceRoot/.cursor/rules/naming-conventions.mdc.template +0 -15
- package/templates/workspaceRoot/.cursor/rules/react-component-style.mdc.template +0 -21
- package/templates/workspaceRoot/.cursor/rules/repo-overview.mdc.template +0 -17
- package/templates/workspaceRoot/.cursor/rules/scalar-modeling.mdc.template +0 -58
- package/templates/workspaceRoot/.cursor/rules/secrets-and-env.mdc.template +0 -13
- package/templates/workspaceRoot/.cursor/rules/service-signal-conventions.mdc.template +0 -83
- package/templates/workspaceRoot/.cursor/rules/srvkit-adapters-and-guards.mdc.template +0 -50
- package/templates/workspaceRoot/.cursor/rules/typescript-imports.mdc.template +0 -16
- /package/{index-r24hmh0q.js → index-yb2wsc7f.js} +0 -0
|
@@ -9,10 +9,10 @@ import {
|
|
|
9
9
|
createTsconfigPackageResolver,
|
|
10
10
|
rewriteBarrelImports,
|
|
11
11
|
transformUseClient
|
|
12
|
-
} from "./index-
|
|
12
|
+
} from "./index-qnv0aqmm.js";
|
|
13
13
|
import {
|
|
14
14
|
__require
|
|
15
|
-
} from "./index-
|
|
15
|
+
} from "./index-yb2wsc7f.js";
|
|
16
16
|
|
|
17
17
|
// pkgs/@akanjs/devkit/frontendBuild/allRoutesBuilder.ts
|
|
18
18
|
import path4 from "path";
|
|
@@ -415,11 +415,21 @@ class AllRoutesBuilder {
|
|
|
415
415
|
const seedIndex = computeRouteSeedIndex(pageEntries);
|
|
416
416
|
this.#app.verbose(`[build-all] discovered ${seedIndex.entries.length} routes`);
|
|
417
417
|
this.#discovery = await GraphClientEntryDiscovery.create(this.#app);
|
|
418
|
+
const allEntries = [];
|
|
419
|
+
const seen = new Set;
|
|
418
420
|
for (const entry of seedIndex.entries) {
|
|
419
421
|
const seeds = Array.from(new Set([...seedIndex.globalLayoutFiles, ...entry.seeds]));
|
|
420
|
-
const
|
|
421
|
-
|
|
422
|
+
for (const discovered of await this.#discovery.discover(seeds)) {
|
|
423
|
+
if (seen.has(discovered))
|
|
424
|
+
continue;
|
|
425
|
+
seen.add(discovered);
|
|
426
|
+
allEntries.push(discovered);
|
|
427
|
+
}
|
|
428
|
+
this.#routeIds.push(entry.routeId);
|
|
422
429
|
}
|
|
430
|
+
this.#app.verbose(`[build-all] ${allEntries.length} client entries across ${this.#routeIds.length} routes`);
|
|
431
|
+
const delta = await this.#buildEntries(allEntries);
|
|
432
|
+
this.#mergeDelta(delta);
|
|
423
433
|
this.#merged.knownEntries = Array.from(this.#knownSet);
|
|
424
434
|
const manifest = {
|
|
425
435
|
routeIds: this.#routeIds,
|
|
@@ -433,23 +443,30 @@ class AllRoutesBuilder {
|
|
|
433
443
|
this.#app.verbose(`[build-all] wrote ${manifestPath} routes=${this.#routeIds.length} entries=${this.#merged.knownEntries.length}`);
|
|
434
444
|
return { manifest, manifestPath, seedIndex };
|
|
435
445
|
}
|
|
436
|
-
async#
|
|
446
|
+
async#buildEntries(entries) {
|
|
437
447
|
if (!this.#discovery)
|
|
438
448
|
throw new Error("[build-all] client entry discovery is not initialized");
|
|
449
|
+
if (entries.length === 0)
|
|
450
|
+
return {
|
|
451
|
+
manifestDelta: {},
|
|
452
|
+
ssrManifestDelta: { moduleLoading: null, moduleMap: {} },
|
|
453
|
+
newEntries: [],
|
|
454
|
+
clientDeps: []
|
|
455
|
+
};
|
|
439
456
|
const started = Date.now();
|
|
440
457
|
const delta = await new RouteClientBuilder({
|
|
441
458
|
app: this.#app,
|
|
442
|
-
|
|
443
|
-
|
|
459
|
+
seeds: [],
|
|
460
|
+
entries,
|
|
444
461
|
artifact: this.#artifact,
|
|
445
462
|
knownEntries: this.#knownSet,
|
|
446
463
|
discovery: this.#discovery,
|
|
447
464
|
command: this.#command
|
|
448
465
|
}).build();
|
|
449
|
-
this.#app.verbose(`[build-all] ${
|
|
466
|
+
this.#app.verbose(`[build-all] bundled ${delta.newEntries.length} entries (${Date.now() - started}ms)`);
|
|
450
467
|
return delta;
|
|
451
468
|
}
|
|
452
|
-
#
|
|
469
|
+
#mergeDelta(delta) {
|
|
453
470
|
for (const [key, row] of Object.entries(delta.manifestDelta))
|
|
454
471
|
this.#merged.clientManifest[key] = row;
|
|
455
472
|
for (const [url, byName] of Object.entries(delta.ssrManifestDelta.moduleMap)) {
|
|
@@ -457,7 +474,6 @@ class AllRoutesBuilder {
|
|
|
457
474
|
}
|
|
458
475
|
for (const abs of delta.newEntries)
|
|
459
476
|
this.#knownSet.add(abs);
|
|
460
|
-
this.#routeIds.push(routeId);
|
|
461
477
|
}
|
|
462
478
|
}
|
|
463
479
|
|
|
@@ -794,10 +810,10 @@ import { compile } from "tailwindcss";
|
|
|
794
810
|
|
|
795
811
|
// pkgs/@akanjs/devkit/frontendBuild/cssCandidateCache.ts
|
|
796
812
|
import { stat } from "fs/promises";
|
|
797
|
-
var CANDIDATE_RE = /-?(?:[\w@]|\[[^\]]+\])[\w:/.-]*(?:\[[^\]]+\][\w:/.-]*)*/g;
|
|
813
|
+
var CANDIDATE_RE = /-?(?:[\w@]|\[[^\]\s]+\])[\w:/.-]*(?:\[[^\]\s]+\][\w:/.-]*)*/g;
|
|
798
814
|
|
|
799
815
|
class CssCandidateCache {
|
|
800
|
-
static #version =
|
|
816
|
+
static #version = 3;
|
|
801
817
|
#path;
|
|
802
818
|
#entries = new Map;
|
|
803
819
|
#dirty = false;
|
|
@@ -957,8 +973,12 @@ class CssImportResolver {
|
|
|
957
973
|
const pkg = await Bun.file(pkgPath).json();
|
|
958
974
|
const subpath = id === pkgName ? "." : `.${id.slice(pkgName.length)}`;
|
|
959
975
|
const exportValue = pkg.exports?.[subpath];
|
|
960
|
-
const
|
|
961
|
-
|
|
976
|
+
const exportedEntry = typeof exportValue === "string" ? exportValue : exportValue?.style || exportValue?.import || exportValue?.default;
|
|
977
|
+
if (exportedEntry)
|
|
978
|
+
return await this.#firstExisting(path7.resolve(pkgDir, exportedEntry));
|
|
979
|
+
if (subpath !== ".")
|
|
980
|
+
return await this.#firstExisting(path7.resolve(pkgDir, subpath));
|
|
981
|
+
return await this.#firstExisting(path7.resolve(pkgDir, pkg.exports?.["."]?.style || pkg.style || "index.css"));
|
|
962
982
|
} catch {
|
|
963
983
|
return null;
|
|
964
984
|
}
|
|
@@ -1015,6 +1035,8 @@ class CssCompiler {
|
|
|
1015
1035
|
#fileExistsCache = new Map;
|
|
1016
1036
|
#resolvedFileCache = new Map;
|
|
1017
1037
|
#resolvedSpecifierCache = new Map;
|
|
1038
|
+
#discoveredCssPaths = new Set;
|
|
1039
|
+
importedStylesheetsByBasePath = {};
|
|
1018
1040
|
#fileExists(absPath) {
|
|
1019
1041
|
let cached = this.#fileExistsCache.get(absPath);
|
|
1020
1042
|
if (!cached) {
|
|
@@ -1048,13 +1070,20 @@ class CssCompiler {
|
|
|
1048
1070
|
async getCss({ refresh } = {}) {
|
|
1049
1071
|
if (this.#cssText !== null && !refresh)
|
|
1050
1072
|
return this.#cssText;
|
|
1073
|
+
this.#discoveredCssPaths.clear();
|
|
1074
|
+
this.importedStylesheetsByBasePath = {};
|
|
1051
1075
|
const { cssPaths, sourcePaths } = await this.discoverCssAndSources({ refresh });
|
|
1052
|
-
|
|
1076
|
+
const { css, imported } = await this.#compileWithImports(cssPaths, sourcePaths);
|
|
1077
|
+
this.#cssText = css;
|
|
1078
|
+
this.importedStylesheetsByBasePath = { "": imported };
|
|
1079
|
+
await this.#warnUnreachableStylesheets();
|
|
1053
1080
|
return this.#cssText;
|
|
1054
1081
|
}
|
|
1055
1082
|
async getCssByBasePath({ refresh } = {}) {
|
|
1056
1083
|
if (this.#cssTextByBasePath !== null && !refresh)
|
|
1057
1084
|
return this.#cssTextByBasePath;
|
|
1085
|
+
this.#discoveredCssPaths.clear();
|
|
1086
|
+
this.importedStylesheetsByBasePath = {};
|
|
1058
1087
|
const akanConfig = await this.#app.getConfig({ refresh });
|
|
1059
1088
|
const pageKeys = await this.#app.getPageKeys({ refresh });
|
|
1060
1089
|
const basePaths = [...akanConfig.basePaths];
|
|
@@ -1065,7 +1094,8 @@ class CssCompiler {
|
|
|
1065
1094
|
return ["", ""];
|
|
1066
1095
|
const started = Date.now();
|
|
1067
1096
|
const { cssPaths, sourcePaths } = await this.discoverCssAndSources({ refresh, pageKeys: rootPageKeys });
|
|
1068
|
-
const css = await this
|
|
1097
|
+
const { css, imported } = await this.#compileWithImports(cssPaths, sourcePaths);
|
|
1098
|
+
this.importedStylesheetsByBasePath[""] = imported;
|
|
1069
1099
|
this.#logger.verbose(`css base=root paths=${cssPaths.length} sources=${sourcePaths.length} in ${Date.now() - started}ms`);
|
|
1070
1100
|
return ["", css];
|
|
1071
1101
|
})(),
|
|
@@ -1075,14 +1105,27 @@ class CssCompiler {
|
|
|
1075
1105
|
return [basePath, ""];
|
|
1076
1106
|
const started = Date.now();
|
|
1077
1107
|
const { cssPaths, sourcePaths } = await this.discoverCssAndSources({ refresh, pageKeys: basePathPageKeys });
|
|
1078
|
-
const css = await this
|
|
1108
|
+
const { css, imported } = await this.#compileWithImports(cssPaths, sourcePaths);
|
|
1109
|
+
this.importedStylesheetsByBasePath[basePath] = imported;
|
|
1079
1110
|
this.#logger.verbose(`css base=${basePath} paths=${cssPaths.length} sources=${sourcePaths.length} in ${Date.now() - started}ms`);
|
|
1080
1111
|
return [basePath, css];
|
|
1081
1112
|
})
|
|
1082
1113
|
]);
|
|
1083
1114
|
this.#cssTextByBasePath = Object.fromEntries(cssEntries);
|
|
1115
|
+
await this.#warnUnreachableStylesheets();
|
|
1084
1116
|
return this.#cssTextByBasePath;
|
|
1085
1117
|
}
|
|
1118
|
+
async#warnUnreachableStylesheets() {
|
|
1119
|
+
const pageDir = path8.join(this.#app.cwdPath, "page");
|
|
1120
|
+
const glob = new Bun.Glob("**/*.css");
|
|
1121
|
+
for await (const cssPath of glob.scan({ cwd: pageDir, absolute: true })) {
|
|
1122
|
+
if (cssPath.includes(`${path8.sep}(libs)${path8.sep}`))
|
|
1123
|
+
continue;
|
|
1124
|
+
if (this.#discoveredCssPaths.has(cssPath))
|
|
1125
|
+
continue;
|
|
1126
|
+
this.#logger.warn(`css ${path8.relative(this.#app.cwdPath, cssPath)} is imported by no route and never compiled`);
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1086
1129
|
async discoverCss({ refresh } = {}) {
|
|
1087
1130
|
const { cssPaths } = await this.discoverCssAndSources({ refresh });
|
|
1088
1131
|
return cssPaths;
|
|
@@ -1142,21 +1185,47 @@ class CssCompiler {
|
|
|
1142
1185
|
queue.push(resolved);
|
|
1143
1186
|
}
|
|
1144
1187
|
}
|
|
1145
|
-
|
|
1188
|
+
const tokenPaths = await this.#libTokenStylesheets(sourceFiles);
|
|
1189
|
+
const cssPaths = [...new Set([...tokenPaths, ...cssFiles])];
|
|
1190
|
+
for (const cssPath of cssPaths)
|
|
1191
|
+
this.#discoveredCssPaths.add(cssPath);
|
|
1192
|
+
return { cssPaths, sourcePaths: [...sourceFiles] };
|
|
1193
|
+
}
|
|
1194
|
+
async#libTokenStylesheets(sourceFiles) {
|
|
1195
|
+
const libsRoot = path8.join(this.#app.workspace.workspaceRoot, "libs");
|
|
1196
|
+
const libNames = new Set;
|
|
1197
|
+
for (const filePath of sourceFiles) {
|
|
1198
|
+
const relPath = path8.relative(libsRoot, filePath);
|
|
1199
|
+
if (relPath.startsWith("..") || path8.isAbsolute(relPath))
|
|
1200
|
+
continue;
|
|
1201
|
+
const [libName] = relPath.split(path8.sep);
|
|
1202
|
+
if (libName)
|
|
1203
|
+
libNames.add(libName);
|
|
1204
|
+
}
|
|
1205
|
+
const tokenPaths = await Promise.all([...libNames].sort().map(async (libName) => {
|
|
1206
|
+
const tokensPath = path8.join(libsRoot, libName, "ui/tokens.css");
|
|
1207
|
+
return await this.#fileExists(tokensPath) ? tokensPath : null;
|
|
1208
|
+
}));
|
|
1209
|
+
return tokenPaths.filter((tokensPath) => !!tokensPath);
|
|
1146
1210
|
}
|
|
1147
1211
|
async compileCss(cssPaths, sourcePaths) {
|
|
1212
|
+
const { css } = await this.#compileWithImports(cssPaths, sourcePaths);
|
|
1213
|
+
return css;
|
|
1214
|
+
}
|
|
1215
|
+
async#compileWithImports(cssPaths, sourcePaths) {
|
|
1148
1216
|
if (cssPaths.length === 0)
|
|
1149
|
-
return "";
|
|
1217
|
+
return { css: "", imported: [] };
|
|
1150
1218
|
const compileStarted = Date.now();
|
|
1151
1219
|
const compilers = await Promise.all(cssPaths.map(async (cssPath) => {
|
|
1152
1220
|
const css = await Bun.file(cssPath).text();
|
|
1153
1221
|
const base = path8.dirname(cssPath);
|
|
1222
|
+
const imported2 = new Map;
|
|
1154
1223
|
const compiler = await compile(css, {
|
|
1155
1224
|
base,
|
|
1156
|
-
loadStylesheet: (id, fromBase) => this.#loadStylesheet(id, fromBase),
|
|
1225
|
+
loadStylesheet: (id, fromBase) => this.#loadStylesheet(id, fromBase, imported2),
|
|
1157
1226
|
loadModule: (id, fromBase) => this.#loadModule(id, fromBase)
|
|
1158
1227
|
});
|
|
1159
|
-
return { cssPath, compiler };
|
|
1228
|
+
return { cssPath, compiler, imported: imported2 };
|
|
1160
1229
|
}));
|
|
1161
1230
|
const sourceDirs = new Set;
|
|
1162
1231
|
for (const entry of compilers) {
|
|
@@ -1169,23 +1238,39 @@ class CssCompiler {
|
|
|
1169
1238
|
const candidates = await this.#scanCandidates(sourcePaths, [...sourceDirs]);
|
|
1170
1239
|
this.#logger.verbose(`css candidates scanned count=${candidates.length} sources=${sourcePaths.length} dirs=${sourceDirs.size} in ${Date.now() - scanStarted}ms`);
|
|
1171
1240
|
const parts = [];
|
|
1241
|
+
const imported = [];
|
|
1172
1242
|
for (const entry of compilers) {
|
|
1173
1243
|
if (!entry)
|
|
1174
1244
|
continue;
|
|
1175
|
-
|
|
1245
|
+
const part = entry.compiler.build(candidates);
|
|
1246
|
+
parts.push(part);
|
|
1247
|
+
for (const [cssPath, content] of entry.imported) {
|
|
1248
|
+
const declaredNames = declaredCustomProperties(content);
|
|
1249
|
+
imported.push({ cssPath, declaredNames });
|
|
1250
|
+
if (declaredNames.length === 0 || declaredNames.some((name) => part.includes(`${name}:`)))
|
|
1251
|
+
continue;
|
|
1252
|
+
this.#logger.warn(`css @import ${cssPath} was loaded by ${entry.cssPath} but none of its ${declaredNames.length} declaration(s) reached the compiled CSS`);
|
|
1253
|
+
}
|
|
1176
1254
|
}
|
|
1177
1255
|
this.#logger.verbose(`css compiled paths=${cssPaths.length} candidates=${candidates.length} in ${Date.now() - compileStarted}ms`);
|
|
1178
|
-
return parts.join(`
|
|
1179
|
-
`);
|
|
1256
|
+
return { css: parts.join(`
|
|
1257
|
+
`), imported };
|
|
1180
1258
|
}
|
|
1181
|
-
async#loadStylesheet(id, fromBase) {
|
|
1259
|
+
async#loadStylesheet(id, fromBase, imported) {
|
|
1182
1260
|
const p = await this.#resolveCssImport(id, fromBase);
|
|
1261
|
+
this.#discoveredCssPaths.add(p);
|
|
1183
1262
|
const content = await Bun.file(p).text();
|
|
1263
|
+
imported?.set(p, content);
|
|
1264
|
+
this.#logger.verbose(`css import "${id}" from ${fromBase} -> ${p} (${content.length} bytes)`);
|
|
1184
1265
|
return { path: p, base: path8.dirname(p), content };
|
|
1185
1266
|
}
|
|
1186
1267
|
async#resolveCssImport(id, fromBase) {
|
|
1187
|
-
if (id.startsWith(".") || id.startsWith("/"))
|
|
1188
|
-
|
|
1268
|
+
if (id.startsWith(".") || id.startsWith("/")) {
|
|
1269
|
+
const filePath = path8.resolve(fromBase, id);
|
|
1270
|
+
if (await this.#fileExists(filePath))
|
|
1271
|
+
return filePath;
|
|
1272
|
+
throw new Error(`[css] failed to resolve stylesheet import "${id}" from ${fromBase} (no file at ${filePath})`);
|
|
1273
|
+
}
|
|
1189
1274
|
const resolver = await this.#getCssImportResolver();
|
|
1190
1275
|
const resolved = await resolver.resolve(id, fromBase);
|
|
1191
1276
|
if (resolved)
|
|
@@ -1270,6 +1355,10 @@ function isSourceFile(filePath) {
|
|
|
1270
1355
|
function isIgnoredNodeModuleSource(filePath) {
|
|
1271
1356
|
return NODE_MODULES_RE.test(filePath) && !AKANJS_NODE_MODULE_RE.test(filePath);
|
|
1272
1357
|
}
|
|
1358
|
+
function declaredCustomProperties(css) {
|
|
1359
|
+
const withoutThemeBlocks = css.replace(/@theme[^{]*\{[^}]*\}/g, "");
|
|
1360
|
+
return [...new Set([...withoutThemeBlocks.matchAll(/(?:^|[\s;{])(--[\w-]+)\s*:/g)].map(([, name]) => name))].filter((name) => !!name);
|
|
1361
|
+
}
|
|
1273
1362
|
function getPageKeyBasePath(pageKey, basePaths) {
|
|
1274
1363
|
const normalized = pageKey.split(path8.sep).join("/").replace(/^\.\//, "");
|
|
1275
1364
|
const segments = normalized.split("/");
|
|
@@ -1747,7 +1836,6 @@ var DEFAULT_EXCLUDE_PREFIX = ["@akanjs/cli/", "@akanjs/devkit/"];
|
|
|
1747
1836
|
var OPTIONAL_BACKEND_EXTERNAL_EXACT = new Set([
|
|
1748
1837
|
"@libsql/client",
|
|
1749
1838
|
"bullmq",
|
|
1750
|
-
"croner",
|
|
1751
1839
|
"ioredis",
|
|
1752
1840
|
"postgres",
|
|
1753
1841
|
"protobufjs"
|
|
@@ -2025,14 +2113,21 @@ function loaderFor2(absPath) {
|
|
|
2025
2113
|
// pkgs/@akanjs/devkit/frontendBuild/precompressArtifacts.ts
|
|
2026
2114
|
import fs2 from "fs";
|
|
2027
2115
|
import path12 from "path";
|
|
2116
|
+
import zlib from "zlib";
|
|
2028
2117
|
var COMPRESSIBLE_EXTS = new Set([".css", ".html", ".js", ".json", ".svg"]);
|
|
2029
2118
|
var MIN_COMPRESS_BYTES = 1024;
|
|
2119
|
+
var BROTLI_QUALITY_BY_EXT = { ".css": 11 };
|
|
2120
|
+
var DEFAULT_BROTLI_QUALITY = 9;
|
|
2121
|
+
var GZIP_LEVEL = 9;
|
|
2030
2122
|
async function precompressArtifacts(app) {
|
|
2031
|
-
const roots = [
|
|
2032
|
-
|
|
2123
|
+
const roots = [
|
|
2124
|
+
path12.join(app.dist.cwdPath, ".akan/artifact/client"),
|
|
2125
|
+
path12.join(app.dist.cwdPath, ".akan/artifact/styles")
|
|
2126
|
+
];
|
|
2127
|
+
const result = { files: 0, inputBytes: 0, outputBytes: 0, brotliBytes: 0 };
|
|
2033
2128
|
await Promise.all(roots.map((root) => precompressRoot(root, result)));
|
|
2034
2129
|
if (result.files > 0) {
|
|
2035
|
-
app.verbose(`[precompress] wrote ${result.files}
|
|
2130
|
+
app.verbose(`[precompress] wrote ${result.files} sidecars (${formatBytes(result.inputBytes)} -> gzip ${formatBytes(result.outputBytes)} / br ${formatBytes(result.brotliBytes)})`);
|
|
2036
2131
|
}
|
|
2037
2132
|
return result;
|
|
2038
2133
|
}
|
|
@@ -2044,15 +2139,27 @@ async function precompressRoot(root, result) {
|
|
|
2044
2139
|
if (!await shouldPrecompress(filePath))
|
|
2045
2140
|
continue;
|
|
2046
2141
|
const bytes = await Bun.file(filePath).bytes();
|
|
2047
|
-
const
|
|
2048
|
-
|
|
2142
|
+
const buffer = toArrayBuffer(bytes);
|
|
2143
|
+
const gz = Bun.gzipSync(buffer, { level: GZIP_LEVEL });
|
|
2144
|
+
const br = brotliCompress(buffer, path12.extname(filePath).toLowerCase());
|
|
2145
|
+
await Promise.all([Bun.write(`${filePath}.gz`, gz), Bun.write(`${filePath}.br`, br)]);
|
|
2049
2146
|
result.files += 1;
|
|
2050
2147
|
result.inputBytes += bytes.byteLength;
|
|
2051
2148
|
result.outputBytes += gz.byteLength;
|
|
2149
|
+
result.brotliBytes += br.byteLength;
|
|
2052
2150
|
}
|
|
2053
2151
|
}
|
|
2152
|
+
function brotliCompress(buffer, ext) {
|
|
2153
|
+
const quality = BROTLI_QUALITY_BY_EXT[ext] ?? DEFAULT_BROTLI_QUALITY;
|
|
2154
|
+
return zlib.brotliCompressSync(buffer, {
|
|
2155
|
+
params: {
|
|
2156
|
+
[zlib.constants.BROTLI_PARAM_QUALITY]: quality,
|
|
2157
|
+
[zlib.constants.BROTLI_PARAM_SIZE_HINT]: buffer.byteLength
|
|
2158
|
+
}
|
|
2159
|
+
});
|
|
2160
|
+
}
|
|
2054
2161
|
async function shouldPrecompress(filePath) {
|
|
2055
|
-
if (filePath.endsWith(".gz"))
|
|
2162
|
+
if (filePath.endsWith(".gz") || filePath.endsWith(".br"))
|
|
2056
2163
|
return false;
|
|
2057
2164
|
if (!COMPRESSIBLE_EXTS.has(path12.extname(filePath).toLowerCase()))
|
|
2058
2165
|
return false;
|
|
@@ -2145,7 +2252,7 @@ class SsrBaseArtifactBuilder {
|
|
|
2145
2252
|
const ssrBundle = await new ClientEntriesBundler({
|
|
2146
2253
|
app: this.#app,
|
|
2147
2254
|
entries: [rscSegmentOutletEntry],
|
|
2148
|
-
...RouteClientBuilder.
|
|
2255
|
+
...RouteClientBuilder.resolveSsrClientBundleOptions(this.#command),
|
|
2149
2256
|
outputSubdir: "client-ssr",
|
|
2150
2257
|
command: this.#command
|
|
2151
2258
|
}).bundle();
|
|
@@ -2197,13 +2304,13 @@ class SsrBaseArtifactBuilder {
|
|
|
2197
2304
|
`);
|
|
2198
2305
|
if (!cssText)
|
|
2199
2306
|
return [];
|
|
2200
|
-
return [this.#writeCssAsset(basePath, cssText)];
|
|
2307
|
+
return [this.#writeCssAsset(basePath, cssText, cssCompiler.importedStylesheetsByBasePath[basePath] ?? [])];
|
|
2201
2308
|
})));
|
|
2202
2309
|
if (optimizedFonts.files.length > 0)
|
|
2203
2310
|
this.#app.verbose(`[base-artifact] optimized ${optimizedFonts.files.length} font files`);
|
|
2204
2311
|
return { cssCompiler, optimizedFonts, cssAssets };
|
|
2205
2312
|
}
|
|
2206
|
-
async#writeCssAsset(basePath, cssText) {
|
|
2313
|
+
async#writeCssAsset(basePath, cssText, imported) {
|
|
2207
2314
|
const cssAssetName = basePath || "root";
|
|
2208
2315
|
const preparedCssText = await prepareCssAsset(this.#command, basePath, cssText);
|
|
2209
2316
|
const cssHash = Bun.hash(`${basePath}
|
|
@@ -2213,9 +2320,17 @@ ${preparedCssText}`).toString(36);
|
|
|
2213
2320
|
`/_akan/styles/${cssAssetName}-${cssHash}.css`
|
|
2214
2321
|
];
|
|
2215
2322
|
await Bun.write(path13.join(this.#absArtifactDir, cssRelPath), preparedCssText);
|
|
2323
|
+
SsrBaseArtifactBuilder.#warnDroppedImports(this.#app, cssRelPath, preparedCssText, imported);
|
|
2216
2324
|
this.#app.verbose(`[base-artifact] wrote ${preparedCssText.length} bytes of CSS for ${basePath} -> ${cssRelPath}`);
|
|
2217
2325
|
return [basePath, { cssUrl, cssRelPath }];
|
|
2218
2326
|
}
|
|
2327
|
+
static #warnDroppedImports(app, cssRelPath, css, imported) {
|
|
2328
|
+
for (const { cssPath, declaredNames } of imported) {
|
|
2329
|
+
if (declaredNames.length === 0 || declaredNames.some((name) => css.includes(`${name}:`)))
|
|
2330
|
+
continue;
|
|
2331
|
+
app.logger.warn(`[base-artifact] @import ${cssPath} declares ${declaredNames.length} custom propert${declaredNames.length === 1 ? "y" : "ies"} and none of them are in ${cssRelPath}`);
|
|
2332
|
+
}
|
|
2333
|
+
}
|
|
2219
2334
|
}
|
|
2220
2335
|
// pkgs/@akanjs/devkit/frontendBuild/styleGuard.ts
|
|
2221
2336
|
var PREFIX = "(?:bg|text|border(?:-[tblrxy])?(?:-[se])?|ring(?:-offset)?|fill|stroke|shadow|from|to|via|divide|outline|decoration|placeholder|caret|accent)";
|
|
@@ -2223,210 +2338,6 @@ var PALETTE = "(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|gre
|
|
|
2223
2338
|
var LEAD = "(^|[\\s\"'`{(\\[:!])";
|
|
2224
2339
|
var TAIL = "(?=[\\s\"'`})\\]:/!,]|$)";
|
|
2225
2340
|
var RAW_PALETTE_RE = new RegExp(`${LEAD}((?:${PREFIX})-)${PALETTE}-\\d{2,3}${TAIL}`, "g");
|
|
2226
|
-
var ARBITRARY_COLOR_RE = /\[(#[0-9a-fA-F]{3,8}|(?:rgb|rgba|hsl|hsla|oklch|oklab|lab|lch|hwb|color)\([^\]]*\))\]/g;
|
|
2227
2341
|
var INTERPOLATED_ARBITRARY_RE = new RegExp(`${LEAD}[a-z][a-z0-9-]*-\\[[^\\]\`]*\\$\\{`, "g");
|
|
2228
|
-
var STYLE_OBJECT_RE = /style=\{\{([\s\S]*?)\}\}/g;
|
|
2229
|
-
var STYLE_TAG_RE = /<style[^>]*>([\s\S]*?)<\/style>/g;
|
|
2230
|
-
var INLINE_COLOR_LITERAL_RE = /#[0-9a-fA-F]{3,8}\b|(?:rgb|rgba|hsl|hsla|oklch|oklab|lab|lch|hwb)\(/g;
|
|
2231
2342
|
var DAISYUI_LEGACY_RE = new RegExp(`${LEAD}(?:` + "btn-(?:primary|secondary|accent|neutral|info|success|warning|error|ghost|link|outline|square|circle|wide|block|xs|sm|md|lg)|" + "badge-(?:primary|secondary|accent|neutral|info|success|warning|error|ghost|outline)|" + "alert-(?:info|success|warning|error)|" + "input-(?:bordered|primary|secondary|accent|ghost|error)|" + "select-(?:bordered|primary|ghost)|textarea-(?:bordered|primary|ghost)|" + "checkbox-(?:primary|secondary|accent)|toggle-(?:primary|secondary|accent)|" + "loading-(?:spinner|dots|ring|ball|bars|infinity)|" + "card-(?:body|title|actions)|modal-(?:box|action|backdrop)|" + "collapse-(?:title|content|arrow|plus)|dropdown-(?:content|end|start|hover)|" + "stat-(?:title|value|desc)|tabs-(?:boxed|lifted|bordered)|tab-active|" + "menu-(?:title|dropdown)|steps-(?:horizontal|vertical)|join-item|" + "mockup-(?:code|phone|browser|window)|drawer-(?:side|content|toggle)" + `)${TAIL}`, "g");
|
|
2232
|
-
var ALL_RULES = [
|
|
2233
|
-
"raw-palette",
|
|
2234
|
-
"arbitrary-color",
|
|
2235
|
-
"inline-color",
|
|
2236
|
-
"daisyui-legacy",
|
|
2237
|
-
"interpolated-arbitrary"
|
|
2238
|
-
];
|
|
2239
|
-
|
|
2240
|
-
class StyleGuard {
|
|
2241
|
-
run(files) {
|
|
2242
|
-
const out = [];
|
|
2243
|
-
for (const file of files) {
|
|
2244
|
-
const directives = this.#parseDirectives(file.content);
|
|
2245
|
-
const scan = this.#stripComments(file.content);
|
|
2246
|
-
const local = [];
|
|
2247
|
-
this.#scanClassLiterals(file, scan, local);
|
|
2248
|
-
this.#scanInlineColors(file, scan, local);
|
|
2249
|
-
for (const v of local)
|
|
2250
|
-
if (!this.#isSuppressed(v, directives))
|
|
2251
|
-
out.push(v);
|
|
2252
|
-
}
|
|
2253
|
-
return out;
|
|
2254
|
-
}
|
|
2255
|
-
#stripComments(src) {
|
|
2256
|
-
const out = src.split("");
|
|
2257
|
-
const blank = (i2) => {
|
|
2258
|
-
if (src[i2] !== `
|
|
2259
|
-
`)
|
|
2260
|
-
out[i2] = " ";
|
|
2261
|
-
};
|
|
2262
|
-
let state = "code";
|
|
2263
|
-
let i = 0;
|
|
2264
|
-
while (i < src.length) {
|
|
2265
|
-
const c = src[i];
|
|
2266
|
-
const d = src[i + 1];
|
|
2267
|
-
if (state === "code") {
|
|
2268
|
-
if (c === "/" && d === "/") {
|
|
2269
|
-
blank(i);
|
|
2270
|
-
blank(i + 1);
|
|
2271
|
-
state = "line";
|
|
2272
|
-
i += 2;
|
|
2273
|
-
} else if (c === "/" && d === "*") {
|
|
2274
|
-
blank(i);
|
|
2275
|
-
blank(i + 1);
|
|
2276
|
-
state = "block";
|
|
2277
|
-
i += 2;
|
|
2278
|
-
} else {
|
|
2279
|
-
if (c === "'")
|
|
2280
|
-
state = "squote";
|
|
2281
|
-
else if (c === '"')
|
|
2282
|
-
state = "dquote";
|
|
2283
|
-
else if (c === "`")
|
|
2284
|
-
state = "template";
|
|
2285
|
-
i++;
|
|
2286
|
-
}
|
|
2287
|
-
} else if (state === "line") {
|
|
2288
|
-
if (c === `
|
|
2289
|
-
`)
|
|
2290
|
-
state = "code";
|
|
2291
|
-
else
|
|
2292
|
-
blank(i);
|
|
2293
|
-
i++;
|
|
2294
|
-
} else if (state === "block") {
|
|
2295
|
-
if (c === "*" && d === "/") {
|
|
2296
|
-
blank(i);
|
|
2297
|
-
blank(i + 1);
|
|
2298
|
-
state = "code";
|
|
2299
|
-
i += 2;
|
|
2300
|
-
} else {
|
|
2301
|
-
blank(i);
|
|
2302
|
-
i++;
|
|
2303
|
-
}
|
|
2304
|
-
} else {
|
|
2305
|
-
const close = state === "squote" ? "'" : state === "dquote" ? '"' : "`";
|
|
2306
|
-
if (c === "\\")
|
|
2307
|
-
i += 2;
|
|
2308
|
-
else {
|
|
2309
|
-
if (c === close)
|
|
2310
|
-
state = "code";
|
|
2311
|
-
i++;
|
|
2312
|
-
}
|
|
2313
|
-
}
|
|
2314
|
-
}
|
|
2315
|
-
return out.join("");
|
|
2316
|
-
}
|
|
2317
|
-
#parseDirectives(content) {
|
|
2318
|
-
const directives = { file: null, nextLine: new Map };
|
|
2319
|
-
const lines = content.split(`
|
|
2320
|
-
`);
|
|
2321
|
-
for (let i = 0;i < lines.length; i++) {
|
|
2322
|
-
const line = lines[i];
|
|
2323
|
-
const nextLineMatch = line.match(/styleguard-disable-next-line\b(.*)$/);
|
|
2324
|
-
if (nextLineMatch) {
|
|
2325
|
-
directives.nextLine.set(i + 2, this.#extractRules(nextLineMatch[1]));
|
|
2326
|
-
continue;
|
|
2327
|
-
}
|
|
2328
|
-
const fileMatch = line.match(/styleguard-disable\b(.*)$/);
|
|
2329
|
-
if (fileMatch)
|
|
2330
|
-
directives.file = this.#mergeFileScope(directives.file, this.#extractRules(fileMatch[1]));
|
|
2331
|
-
}
|
|
2332
|
-
return directives;
|
|
2333
|
-
}
|
|
2334
|
-
#extractRules(tail) {
|
|
2335
|
-
const found = ALL_RULES.filter((rule) => tail.includes(rule));
|
|
2336
|
-
return found.length === 0 ? "all" : new Set(found);
|
|
2337
|
-
}
|
|
2338
|
-
#mergeFileScope(prev, next) {
|
|
2339
|
-
if (prev === "all" || next === "all")
|
|
2340
|
-
return "all";
|
|
2341
|
-
return new Set([...prev ?? [], ...next]);
|
|
2342
|
-
}
|
|
2343
|
-
#isSuppressed(v, directives) {
|
|
2344
|
-
if (directives.file === "all" || directives.file?.has(v.rule))
|
|
2345
|
-
return true;
|
|
2346
|
-
const nextLine = directives.nextLine.get(v.line);
|
|
2347
|
-
return nextLine === "all" || (nextLine?.has(v.rule) ?? false);
|
|
2348
|
-
}
|
|
2349
|
-
#scanClassLiterals(file, scan, out) {
|
|
2350
|
-
for (const m of scan.matchAll(RAW_PALETTE_RE)) {
|
|
2351
|
-
out.push(this.#violation(file, m.index ?? 0, {
|
|
2352
|
-
rule: "raw-palette",
|
|
2353
|
-
severity: "error",
|
|
2354
|
-
suggestion: "\uC2DC\uB9E8\uD2F1 \uD1A0\uD070\uC73C\uB85C \uAD50\uCCB4\uD558\uC138\uC694 \u2014 \uC608: text-gray-500\u2192text-muted-foreground, bg-red-500\u2192bg-destructive, bg-blue-500\u2192bg-info. \uBE0C\uB79C\uB4DC \uC0C9\uC740 page/styles.css \uD1A0\uD070\uC5D0\uC11C \uC870\uC815\uD569\uB2C8\uB2E4."
|
|
2355
|
-
}));
|
|
2356
|
-
}
|
|
2357
|
-
for (const m of scan.matchAll(ARBITRARY_COLOR_RE)) {
|
|
2358
|
-
out.push(this.#violation(file, m.index ?? 0, {
|
|
2359
|
-
rule: "arbitrary-color",
|
|
2360
|
-
severity: "error",
|
|
2361
|
-
suggestion: "\uC784\uC758 \uC0C9 \uB9AC\uD130\uB7F4 \uB300\uC2E0 \uD1A0\uD070\uC744 \uC4F0\uC138\uC694 \u2014 bg-[#3b82f6]\u2192bg-info, text-[rgb(...)]\u2192\uC2DC\uB9E8\uD2F1 \uD1A0\uD070. \uAF2D \uD544\uC694\uD558\uBA74 \uC2A4\uCF54\uD504 \uD1A0\uD070(.campaign-x { --primary: \u2026 })\uC744 \uC815\uC758\uD558\uC138\uC694."
|
|
2362
|
-
}));
|
|
2363
|
-
}
|
|
2364
|
-
for (const m of scan.matchAll(DAISYUI_LEGACY_RE)) {
|
|
2365
|
-
out.push(this.#violation(file, m.index ?? 0, {
|
|
2366
|
-
rule: "daisyui-legacy",
|
|
2367
|
-
severity: "error",
|
|
2368
|
-
suggestion: "daisyUI \uD074\uB798\uC2A4\uB294 \uC81C\uAC70\uB410\uC2B5\uB2C8\uB2E4. akanjs/ui \uD504\uB9AC\uBBF8\uD2F0\uBE0C(Button/Badge \uB4F1)\uB098 buttonRecipe()/badgeRecipe() + \uC2DC\uB9E8\uD2F1 \uD1A0\uD070\uC73C\uB85C \uAD50\uCCB4\uD558\uC138\uC694."
|
|
2369
|
-
}));
|
|
2370
|
-
}
|
|
2371
|
-
for (const m of scan.matchAll(INTERPOLATED_ARBITRARY_RE)) {
|
|
2372
|
-
out.push(this.#violation(file, m.index ?? 0, {
|
|
2373
|
-
rule: "interpolated-arbitrary",
|
|
2374
|
-
severity: "error",
|
|
2375
|
-
suggestion: "\uB7F0\uD0C0\uC784 \uAC12\uC73C\uB85C \uC784\uC758\uAC12 \uD074\uB798\uC2A4\uB97C \uC870\uB9BD\uD558\uBA74 CSS \uAC00 \uC0DD\uC131\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4(\uC2A4\uCE90\uB108\uB294 \uC18C\uC2A4 \uD14D\uC2A4\uD2B8\uB97C \uC77D\uC2B5\uB2C8\uB2E4). \uD06C\uAE30/\uC704\uCE58\uB294 style prop \uC73C\uB85C \uB118\uAE30\uC138\uC694 \u2014 style={{ minHeight }}. \uAC12\uC774 enum \uC774\uBA74 \uB9AC\uD130\uB7F4 \uD074\uB798\uC2A4 \uB9F5\uC73C\uB85C \uB450\uC138\uC694."
|
|
2376
|
-
}));
|
|
2377
|
-
}
|
|
2378
|
-
}
|
|
2379
|
-
#scanInlineColors(file, scan, out) {
|
|
2380
|
-
const push = (index) => out.push(this.#violation(file, index, {
|
|
2381
|
-
rule: "inline-color",
|
|
2382
|
-
severity: "error",
|
|
2383
|
-
suggestion: "\uC778\uB77C\uC778 \uC0C9 \uB9AC\uD130\uB7F4 \uB300\uC2E0 CSS \uBCC0\uC218/\uD1A0\uD070\uC744 \uCC38\uC870\uD558\uC138\uC694 \u2014 style={{ color: 'var(--primary)' }} \uB610\uB294 \uC2DC\uB9E8\uD2F1 \uD074\uB798\uC2A4(text-primary). \uD558\uB4DC\uCF54\uB529 hex/rgb \uB294 \uD14C\uB9C8 \uC804\uD658\uC744 \uAE68\uB728\uB9BD\uB2C8\uB2E4."
|
|
2384
|
-
}));
|
|
2385
|
-
for (const region of [STYLE_OBJECT_RE, STYLE_TAG_RE]) {
|
|
2386
|
-
for (const m of scan.matchAll(region)) {
|
|
2387
|
-
const inner = m[1] ?? "";
|
|
2388
|
-
const innerStart = (m.index ?? 0) + m[0].indexOf(inner);
|
|
2389
|
-
for (const lit of inner.matchAll(INLINE_COLOR_LITERAL_RE)) {
|
|
2390
|
-
push(innerStart + (lit.index ?? 0));
|
|
2391
|
-
}
|
|
2392
|
-
}
|
|
2393
|
-
}
|
|
2394
|
-
}
|
|
2395
|
-
#violation(file, index, base) {
|
|
2396
|
-
const before = file.content.slice(0, index);
|
|
2397
|
-
const line = before.length === 0 ? 1 : before.split(`
|
|
2398
|
-
`).length;
|
|
2399
|
-
const lineStart = before.lastIndexOf(`
|
|
2400
|
-
`) + 1;
|
|
2401
|
-
const lineEnd = file.content.indexOf(`
|
|
2402
|
-
`, index);
|
|
2403
|
-
const snippet = file.content.slice(lineStart, lineEnd === -1 ? undefined : lineEnd).trim();
|
|
2404
|
-
return { ...base, path: file.path, line, snippet };
|
|
2405
|
-
}
|
|
2406
|
-
static countNamedComponentClasses(css) {
|
|
2407
|
-
const names = new Set;
|
|
2408
|
-
const layerRe = /@layer\s+components\s*\{/g;
|
|
2409
|
-
for (const layer of css.matchAll(layerRe)) {
|
|
2410
|
-
const bodyStart = (layer.index ?? 0) + layer[0].length;
|
|
2411
|
-
const body = StyleGuard.#extractBalanced(css, bodyStart);
|
|
2412
|
-
for (const sel of body.matchAll(/(^|[\s}])\.(-?[a-zA-Z_][\w-]*)/g))
|
|
2413
|
-
names.add(sel[2]);
|
|
2414
|
-
}
|
|
2415
|
-
return { count: names.size, names: [...names].sort() };
|
|
2416
|
-
}
|
|
2417
|
-
static #extractBalanced(css, start) {
|
|
2418
|
-
let depth = 1;
|
|
2419
|
-
for (let i = start;i < css.length; i++) {
|
|
2420
|
-
const ch = css[i];
|
|
2421
|
-
if (ch === "{")
|
|
2422
|
-
depth++;
|
|
2423
|
-
else if (ch === "}") {
|
|
2424
|
-
depth--;
|
|
2425
|
-
if (depth === 0)
|
|
2426
|
-
return css.slice(start, i);
|
|
2427
|
-
}
|
|
2428
|
-
}
|
|
2429
|
-
return css.slice(start);
|
|
2430
|
-
}
|
|
2431
|
-
}
|
|
2432
2343
|
export { AllRoutesBuilder, CsrArtifactBuilder, CssCompiler, FontOptimizer, PagesBundleBuilder, precompressArtifacts, SsrBaseArtifactBuilder };
|