@dereekb/dbx-cli 13.42.0 → 14.0.0
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/eslint/index.esm.js +2 -2
- package/eslint/package.json +7 -7
- package/firebase-api-manifest/main.js +22 -4
- package/firebase-api-manifest/package.json +5 -5
- package/firestore-query-manifest/main.js +9 -7
- package/firestore-query-manifest/package.json +4 -4
- package/generate-firestore-indexes/main.js +3 -3
- package/generate-firestore-indexes/package.json +3 -3
- package/generate-mcp-manifest/package.json +4 -4
- package/generate-route-manifest/main.js +1 -1
- package/generate-route-manifest/package.json +3 -3
- package/index.esm.js +2835 -2002
- package/lint-cache/package.json +3 -3
- package/manifest-extract/index.esm.js +6 -12
- package/manifest-extract/package.json +9 -10
- package/model-test/index.esm.js +61 -63
- package/model-test/package.json +3 -3
- package/package.json +14 -14
- package/route/package.json +7 -7
- package/src/lib/doctor/build-drift.check.d.ts +21 -0
- package/src/lib/doctor/doctor.command.factory.d.ts +21 -2
- package/src/lib/doctor/firestore-session.check.d.ts +7 -1
- package/src/lib/doctor/index.d.ts +1 -0
- package/src/lib/firestore/firestore.sdk-identity.d.ts +193 -0
- package/src/lib/firestore/index.d.ts +1 -0
- package/src/lib/manifest/types.d.ts +21 -0
- package/src/lib/mcp-scan/manifest/css-utilities-schema.d.ts +2 -2
- package/src/lib/mcp-scan/manifest/dbx-docs-ui-examples-schema.d.ts +2 -2
- package/src/lib/mcp-scan/manifest/ui-components-schema.d.ts +2 -2
- package/src/lib/mcp-scan/scan/dbx-docs-ui-examples-extract.d.ts +1 -1
- package/src/lib/mcp-scan/scan/ui-components-extract.d.ts +1 -1
- package/src/lib/runner/build-stamp.d.ts +138 -0
- package/src/lib/runner/index.d.ts +1 -0
- package/src/lib/runner/run.d.ts +13 -0
- package/test/index.esm.js +19 -22
- package/test/package.json +11 -11
- package/validate/index.js +1069 -76
- package/validate/package.json +4 -4
package/validate/index.js
CHANGED
|
@@ -2909,6 +2909,96 @@ var RULE_CATALOG = [
|
|
|
2909
2909
|
whenItApplies: "When a key on `<app>SystemStateStoredDataConverterMap` is neither a local `<NAME>_SYSTEM_STATE_TYPE` constant nor an imported identifier.",
|
|
2910
2910
|
whenItDoesNotApply: "Keys imported from upstream packages \u2014 those are detected via `importedIdentifiers` and skipped.",
|
|
2911
2911
|
canonicalFix: "Either declare the missing constant locally or import it from the upstream package that owns it."
|
|
2912
|
+
},
|
|
2913
|
+
{
|
|
2914
|
+
code: "WORKSPACE_DEPLOY_ENVIRONMENT_FILE_MISSING",
|
|
2915
|
+
source: "dbx_workspace_validate",
|
|
2916
|
+
severity: "error",
|
|
2917
|
+
title: "A configuration's file-replacement or esbuild-config path does not exist on disk",
|
|
2918
|
+
whatItFlags: "A configuration's file-replacement or esbuild-config path does not exist on disk.",
|
|
2919
|
+
whenItApplies: "When a `fileReplacements` `replace` / `with` path, or a configuration's `esbuildConfig` path, names a file that is absent from the working tree.",
|
|
2920
|
+
whenItDoesNotApply: "Generated files produced by an earlier target in the same pipeline \u2014 declare them as that target's `outputs` so the dependency is explicit, or the reference is genuinely broken.",
|
|
2921
|
+
canonicalFix: "Create the missing file or correct the path. An Angular build fails loudly on a missing replacement, but an esbuild `additionalProperties: true` schema accepts a bad path silently."
|
|
2922
|
+
},
|
|
2923
|
+
{
|
|
2924
|
+
code: "WORKSPACE_DEPLOY_ENVIRONMENT_FILE_UNREFERENCED",
|
|
2925
|
+
source: "dbx_workspace_validate",
|
|
2926
|
+
severity: "warning",
|
|
2927
|
+
title: "An `environment.<name>.ts` file exists but no build configuration references it",
|
|
2928
|
+
whatItFlags: "An `environment.<name>.ts` file exists but no build configuration references it.",
|
|
2929
|
+
whenItApplies: "When a file matching `environment.*.ts` sits in a project's `src/environments/` directory and appears in no configuration's `fileReplacements`, and no `esbuildConfig` file references it.",
|
|
2930
|
+
whenItDoesNotApply: "The base `environment.ts` (always the replacement source, never a target), and environment files imported directly by application code rather than swapped in by the build.",
|
|
2931
|
+
canonicalFix: "Add the configuration that consumes it, or delete the file. An unreachable environment file reads as a working deploy lane that does not exist \u2014 the file is edited, reviewed, and never compiled."
|
|
2932
|
+
},
|
|
2933
|
+
{
|
|
2934
|
+
code: "WORKSPACE_DEPLOY_HOSTING_TARGET_MISSING",
|
|
2935
|
+
source: "dbx_workspace_validate",
|
|
2936
|
+
severity: "error",
|
|
2937
|
+
title: "A deploy command targets a firebase hosting target that `firebase.json` does not declare",
|
|
2938
|
+
whatItFlags: "A deploy command targets a firebase hosting target that `firebase.json` does not declare.",
|
|
2939
|
+
whenItApplies: "When a deploy command runs `firebase deploy --only hosting:<target>` and `<target>` matches no `target` (or `site`) entry under `hosting` in `firebase.json`.",
|
|
2940
|
+
whenItDoesNotApply: "Workspaces that resolve hosting targets through `.firebaserc` target aliases the scanner does not read \u2014 verify manually if this misfires.",
|
|
2941
|
+
canonicalFix: "Add the hosting entry to `firebase.json`, or point the command at a declared target. `firebase deploy` fails on an unknown hosting target, so this breaks the lane outright."
|
|
2942
|
+
},
|
|
2943
|
+
{
|
|
2944
|
+
code: "WORKSPACE_DEPLOY_LANE_ASYMMETRIC",
|
|
2945
|
+
source: "dbx_workspace_validate",
|
|
2946
|
+
severity: "warning",
|
|
2947
|
+
title: "A deploy lane is wired on one side of the app/api pair but not the other",
|
|
2948
|
+
whatItFlags: "A deploy lane is wired on one side of the app/api pair but not the other.",
|
|
2949
|
+
whenItApplies: "When one project in the workspace declares `ci-deploy-<lane>` and a sibling project that declares at least one other `ci-deploy-*` lane does not declare this one.",
|
|
2950
|
+
whenItDoesNotApply: "Projects deliberately deployed on their own cadence, and workspaces with a single deployable project. Suppress by giving the sibling the lane, or accept the warning.",
|
|
2951
|
+
canonicalFix: "Add the missing `ci-deploy-<lane>` target to the sibling project. A lane present on the API but not the web app (or vice versa) deploys half an application and leaves the two halves on different builds."
|
|
2952
|
+
},
|
|
2953
|
+
{
|
|
2954
|
+
code: "WORKSPACE_DEPLOY_LANE_NO_ENVIRONMENT_SELECTION",
|
|
2955
|
+
source: "dbx_workspace_validate",
|
|
2956
|
+
severity: "error",
|
|
2957
|
+
title: "A deploy lane builds a configuration that selects no environment file",
|
|
2958
|
+
whatItFlags: "A deploy lane builds a configuration that selects no environment file.",
|
|
2959
|
+
whenItApplies: "When a `ci-deploy-<lane>` target builds a configuration that provides no environment-selection mechanism for its executor \u2014 no `fileReplacements` for an Angular `application` build, or no lane-specific `esbuildConfig` for an `@nx/esbuild:esbuild` build.",
|
|
2960
|
+
whenItDoesNotApply: "Non-deploy configurations such as `development`, which are expected to compile the base `environment.ts`. Only configurations reachable from a `ci-deploy-*` target are checked.",
|
|
2961
|
+
canonicalFix: "Add the `fileReplacements` entry (or the lane's `esbuildConfig`) to the configuration. Without it the lane deploys the unreplaced `environment.ts` \u2014 localhost URLs, dev credentials, and any developer-only function map \u2014 to a live project."
|
|
2962
|
+
},
|
|
2963
|
+
{
|
|
2964
|
+
code: "WORKSPACE_TARGET_OUTPUT_ESCAPES_ROOT",
|
|
2965
|
+
source: "dbx_workspace_validate",
|
|
2966
|
+
severity: "error",
|
|
2967
|
+
title: "A target's `outputs` entry resolves outside the workspace root",
|
|
2968
|
+
whatItFlags: "A target's `outputs` entry resolves outside the workspace root.",
|
|
2969
|
+
whenItApplies: 'When an `outputs` entry \u2014 after `{options.<name>}` substitution from the target\'s own options \u2014 resolves to a path above the workspace root. The common shape is `outputs: ["{options.reportsDirectory}"]` with a `../../`-relative option value.',
|
|
2970
|
+
whenItDoesNotApply: "Entries anchored by `{workspaceRoot}` or `{projectRoot}`, and entries whose `{options.<name>}` token has no resolvable value (skipped rather than guessed).",
|
|
2971
|
+
canonicalFix: 'Anchor the entry explicitly, e.g. `"{workspaceRoot}/coverage/{projectRoot}"`. Nx refuses to cache an output it cannot place inside the workspace and will report the task as flaky instead of failing loudly.'
|
|
2972
|
+
},
|
|
2973
|
+
{
|
|
2974
|
+
code: "WORKSPACE_TARGET_REF_CONFIGURATION_MISSING",
|
|
2975
|
+
source: "dbx_workspace_validate",
|
|
2976
|
+
severity: "error",
|
|
2977
|
+
title: "An `nx run <project>:<target>:<configuration>` reference names a configuration the target does not declare",
|
|
2978
|
+
whatItFlags: "An `nx run <project>:<target>:<configuration>` reference names a configuration the target does not declare.",
|
|
2979
|
+
whenItApplies: "Whenever a reference carries a configuration segment and the target's `configurations` map has no such key.",
|
|
2980
|
+
whenItDoesNotApply: "Targets that declare no `configurations` at all \u2014 Nx tolerates a configuration argument there, so it is not reported.",
|
|
2981
|
+
canonicalFix: "Rename the reference to a declared configuration (a `prod` / `production` mismatch is the usual cause), or add the configuration to the target. Nx does NOT error on an unknown configuration: `create-task-graph.js` silently substitutes `defaultConfiguration`, so a mismatch here ships whatever that default builds \u2014 and ships the base options with no configuration merged at all when no default is set."
|
|
2982
|
+
},
|
|
2983
|
+
{
|
|
2984
|
+
code: "WORKSPACE_TARGET_REF_PROJECT_MISSING",
|
|
2985
|
+
source: "dbx_workspace_validate",
|
|
2986
|
+
severity: "error",
|
|
2987
|
+
title: "An `nx run <project>:...` reference names a project that does not exist in the workspace",
|
|
2988
|
+
whatItFlags: "An `nx run <project>:...` reference names a project that does not exist in the workspace.",
|
|
2989
|
+
whenItApplies: "When a target reference in a `project.json` command, CI config, shell script, or npm script names a project with no matching entry in the workspace project graph.",
|
|
2990
|
+
whenItDoesNotApply: "References built from shell variables or CI interpolation the scanner cannot resolve \u2014 those are skipped rather than reported.",
|
|
2991
|
+
canonicalFix: "Create the project, or update the reference to the project's real name. A renamed or deleted project leaves these references behind and they only fail at deploy time."
|
|
2992
|
+
},
|
|
2993
|
+
{
|
|
2994
|
+
code: "WORKSPACE_TARGET_REF_TARGET_MISSING",
|
|
2995
|
+
source: "dbx_workspace_validate",
|
|
2996
|
+
severity: "error",
|
|
2997
|
+
title: "An `nx run <project>:<target>` reference names a target the project does not declare",
|
|
2998
|
+
whatItFlags: "An `nx run <project>:<target>` reference names a target the project does not declare.",
|
|
2999
|
+
whenItApplies: "When the referenced project exists but has no target by that name (after `nx.json` `targetDefaults` are accounted for).",
|
|
3000
|
+
whenItDoesNotApply: "Targets contributed by an inferred-target plugin the scanner does not evaluate \u2014 declare the plugin's targets explicitly if this misfires.",
|
|
3001
|
+
canonicalFix: "Add the target to the project, or point the reference at an existing one. This is the single most common way a CI lane silently stops doing anything."
|
|
2912
3002
|
}
|
|
2913
3003
|
];
|
|
2914
3004
|
|
|
@@ -2996,22 +3086,22 @@ function formatSeeAlso(ref) {
|
|
|
2996
3086
|
// packages/dbx-cli/validate/src/lib/_core/validate-input.ts
|
|
2997
3087
|
import { glob as fsGlob, readFile, stat } from "node:fs/promises";
|
|
2998
3088
|
import { resolve, sep } from "node:path";
|
|
2999
|
-
function ensurePathInsideCwd(
|
|
3089
|
+
function ensurePathInsideCwd(relative13, cwd) {
|
|
3000
3090
|
const cwdPrefix = cwd.endsWith(sep) ? cwd : cwd + sep;
|
|
3001
|
-
const absolute = resolve(cwd,
|
|
3091
|
+
const absolute = resolve(cwd, relative13);
|
|
3002
3092
|
if (!absolute.startsWith(cwdPrefix) && absolute !== cwd) {
|
|
3003
|
-
throw new Error(`Path \`${
|
|
3093
|
+
throw new Error(`Path \`${relative13}\` resolves outside the server cwd and is not allowed.`);
|
|
3004
3094
|
}
|
|
3005
3095
|
}
|
|
3006
3096
|
async function resolveFolderPaths(config) {
|
|
3007
3097
|
const { paths, glob, cwd } = config;
|
|
3008
3098
|
const collected = [];
|
|
3009
3099
|
const seen = /* @__PURE__ */ new Set();
|
|
3010
|
-
const accept = (
|
|
3011
|
-
if (seen.has(
|
|
3012
|
-
ensurePathInsideCwd(
|
|
3013
|
-
seen.add(
|
|
3014
|
-
collected.push(
|
|
3100
|
+
const accept = (relative13) => {
|
|
3101
|
+
if (seen.has(relative13)) return;
|
|
3102
|
+
ensurePathInsideCwd(relative13, cwd);
|
|
3103
|
+
seen.add(relative13);
|
|
3104
|
+
collected.push(relative13);
|
|
3015
3105
|
};
|
|
3016
3106
|
if (paths) {
|
|
3017
3107
|
for (const p of paths) {
|
|
@@ -3054,13 +3144,13 @@ async function resolveValidatorSources(config) {
|
|
|
3054
3144
|
pathList.push(match);
|
|
3055
3145
|
}
|
|
3056
3146
|
}
|
|
3057
|
-
for (const
|
|
3058
|
-
if (seenNames.has(
|
|
3059
|
-
ensurePathInsideCwd(
|
|
3060
|
-
const absolute = resolve(cwd,
|
|
3147
|
+
for (const relative13 of pathList) {
|
|
3148
|
+
if (seenNames.has(relative13)) continue;
|
|
3149
|
+
ensurePathInsideCwd(relative13, cwd);
|
|
3150
|
+
const absolute = resolve(cwd, relative13);
|
|
3061
3151
|
const text = await readFile(absolute, "utf8");
|
|
3062
|
-
seenNames.add(
|
|
3063
|
-
collected.push({ name:
|
|
3152
|
+
seenNames.add(relative13);
|
|
3153
|
+
collected.push({ name: relative13, text });
|
|
3064
3154
|
}
|
|
3065
3155
|
return collected;
|
|
3066
3156
|
}
|
|
@@ -3115,12 +3205,12 @@ function formatRemediationBlock(hint) {
|
|
|
3115
3205
|
return lines.join("\n");
|
|
3116
3206
|
}
|
|
3117
3207
|
function aggregateFolderRules(config) {
|
|
3118
|
-
const { inspections, runRules:
|
|
3208
|
+
const { inspections, runRules: runRules10 } = config;
|
|
3119
3209
|
const violations = [];
|
|
3120
3210
|
let errorCount = 0;
|
|
3121
3211
|
let warningCount = 0;
|
|
3122
3212
|
for (const inspection of inspections) {
|
|
3123
|
-
const folderViolations =
|
|
3213
|
+
const folderViolations = runRules10(inspection);
|
|
3124
3214
|
for (const v of folderViolations) {
|
|
3125
3215
|
violations.push(v);
|
|
3126
3216
|
if (v.severity === "error") {
|
|
@@ -6335,6 +6425,11 @@ var RESERVED_MODEL_FOLDERS = [
|
|
|
6335
6425
|
reason: "StorageFile is a canonical group from `@dereekb/firebase` with a richer layout than the base 5 files (group, upload, file, etc.); downstream projects extend rather than redeclare it.",
|
|
6336
6426
|
recommendedTool: "dbx_storagefile_m_validate_folder"
|
|
6337
6427
|
},
|
|
6428
|
+
{
|
|
6429
|
+
name: "formspace",
|
|
6430
|
+
reason: "FormSpace is a canonical group from `@dereekb/firebase` with a richer layout than the base 5 files (type, upload, task, util, etc.); a downstream project only registers its own types and does not redeclare the model.",
|
|
6431
|
+
recommendedTool: "dbx_model_validate_folder"
|
|
6432
|
+
},
|
|
6338
6433
|
{
|
|
6339
6434
|
name: "calendar",
|
|
6340
6435
|
reason: "Calendar is a canonical group from `@dereekb/firebase` with a richer layout than the base 5 files (type, expand, ics, processing, etc.); downstream projects extend rather than redeclare it.",
|
|
@@ -12734,6 +12829,901 @@ function appendDiagnosticEntry(lines, d) {
|
|
|
12734
12829
|
}
|
|
12735
12830
|
}
|
|
12736
12831
|
|
|
12832
|
+
// packages/dbx-cli/validate/src/lib/workspace-validate/index.ts
|
|
12833
|
+
var workspace_validate_exports = {};
|
|
12834
|
+
__export(workspace_validate_exports, {
|
|
12835
|
+
DEPLOY_TARGET_PREFIX: () => DEPLOY_TARGET_PREFIX,
|
|
12836
|
+
WORKSPACE_RULE_GROUPS: () => WORKSPACE_RULE_GROUPS,
|
|
12837
|
+
WorkspaceValidateCode: () => WorkspaceValidateCode,
|
|
12838
|
+
formatResult: () => formatResult11,
|
|
12839
|
+
inspectWorkspace: () => inspectWorkspace,
|
|
12840
|
+
pathEscapesWorkspaceRoot: () => pathEscapesWorkspaceRoot,
|
|
12841
|
+
scanDocumentTargetReferences: () => scanDocumentTargetReferences,
|
|
12842
|
+
scanHostingTargets: () => scanHostingTargets,
|
|
12843
|
+
scanTargetReferences: () => scanTargetReferences,
|
|
12844
|
+
validateWorkspace: () => validateWorkspace
|
|
12845
|
+
});
|
|
12846
|
+
|
|
12847
|
+
// packages/dbx-cli/validate/src/lib/workspace-validate/codes.ts
|
|
12848
|
+
var WorkspaceValidateCode = /* @__PURE__ */ ((WorkspaceValidateCode2) => {
|
|
12849
|
+
WorkspaceValidateCode2["WORKSPACE_TARGET_REF_PROJECT_MISSING"] = "WORKSPACE_TARGET_REF_PROJECT_MISSING";
|
|
12850
|
+
WorkspaceValidateCode2["WORKSPACE_TARGET_REF_TARGET_MISSING"] = "WORKSPACE_TARGET_REF_TARGET_MISSING";
|
|
12851
|
+
WorkspaceValidateCode2["WORKSPACE_TARGET_REF_CONFIGURATION_MISSING"] = "WORKSPACE_TARGET_REF_CONFIGURATION_MISSING";
|
|
12852
|
+
WorkspaceValidateCode2["WORKSPACE_TARGET_OUTPUT_ESCAPES_ROOT"] = "WORKSPACE_TARGET_OUTPUT_ESCAPES_ROOT";
|
|
12853
|
+
WorkspaceValidateCode2["WORKSPACE_DEPLOY_LANE_NO_ENVIRONMENT_SELECTION"] = "WORKSPACE_DEPLOY_LANE_NO_ENVIRONMENT_SELECTION";
|
|
12854
|
+
WorkspaceValidateCode2["WORKSPACE_DEPLOY_ENVIRONMENT_FILE_MISSING"] = "WORKSPACE_DEPLOY_ENVIRONMENT_FILE_MISSING";
|
|
12855
|
+
WorkspaceValidateCode2["WORKSPACE_DEPLOY_ENVIRONMENT_FILE_UNREFERENCED"] = "WORKSPACE_DEPLOY_ENVIRONMENT_FILE_UNREFERENCED";
|
|
12856
|
+
WorkspaceValidateCode2["WORKSPACE_DEPLOY_HOSTING_TARGET_MISSING"] = "WORKSPACE_DEPLOY_HOSTING_TARGET_MISSING";
|
|
12857
|
+
WorkspaceValidateCode2["WORKSPACE_DEPLOY_LANE_ASYMMETRIC"] = "WORKSPACE_DEPLOY_LANE_ASYMMETRIC";
|
|
12858
|
+
return WorkspaceValidateCode2;
|
|
12859
|
+
})(WorkspaceValidateCode || {});
|
|
12860
|
+
|
|
12861
|
+
// packages/dbx-cli/validate/src/lib/workspace-validate/format.ts
|
|
12862
|
+
function formatResult11(result) {
|
|
12863
|
+
return formatFileGroupedResult({
|
|
12864
|
+
title: "Workspace convention validation",
|
|
12865
|
+
summary: `Checked ${result.projectsChecked} project(s) and ${result.referencesChecked} target reference(s) across group(s) [${result.groups.join(", ")}].`,
|
|
12866
|
+
innerKey: (violation) => violation.project,
|
|
12867
|
+
result
|
|
12868
|
+
});
|
|
12869
|
+
}
|
|
12870
|
+
|
|
12871
|
+
// packages/dbx-cli/validate/src/lib/workspace-validate/inspect.ts
|
|
12872
|
+
import { readdir as readdir8, readFile as readFile11, stat as stat10 } from "node:fs/promises";
|
|
12873
|
+
import { basename as basename5, dirname as dirname2, join as join10, relative as relative6, resolve as resolve3 } from "node:path";
|
|
12874
|
+
|
|
12875
|
+
// packages/dbx-cli/validate/src/lib/workspace-validate/references.ts
|
|
12876
|
+
var NX_SUBCOMMANDS = /* @__PURE__ */ new Set(["run", "run-many", "affected", "affected:graph", "affected:test", "affected:build", "graph", "dep-graph", "print-affected", "show", "list", "report", "migrate", "reset", "repair", "generate", "g", "add", "init", "import", "sync", "release", "format", "format:check", "format:write", "daemon", "connect", "login", "logout", "view-logs", "exec", "watch", "documentation", "help", "register", "record", "start-ci-run"]);
|
|
12877
|
+
var INTERPOLATION_PATTERN = /[$`{}()*?]|\\\\/;
|
|
12878
|
+
var TOKEN_LEADING_CHARS = `"'`;
|
|
12879
|
+
var TOKEN_TRAILING_CHARS = `"',;`;
|
|
12880
|
+
var PROJECT_TOKEN_PATTERN = /^[a-zA-Z0-9@][a-zA-Z0-9@/_.-]*$/;
|
|
12881
|
+
var TARGET_TOKEN_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9_.:-]*$/;
|
|
12882
|
+
function isFlag(token) {
|
|
12883
|
+
return token.startsWith("-");
|
|
12884
|
+
}
|
|
12885
|
+
function isResolvable(token) {
|
|
12886
|
+
return token.length > 0 && !INTERPOLATION_PATTERN.test(token);
|
|
12887
|
+
}
|
|
12888
|
+
function trimTokenPunctuation(raw) {
|
|
12889
|
+
let start = 0;
|
|
12890
|
+
let end = raw.length;
|
|
12891
|
+
while (start < end && TOKEN_LEADING_CHARS.includes(raw[start])) {
|
|
12892
|
+
start += 1;
|
|
12893
|
+
}
|
|
12894
|
+
while (end > start && TOKEN_TRAILING_CHARS.includes(raw[end - 1])) {
|
|
12895
|
+
end -= 1;
|
|
12896
|
+
}
|
|
12897
|
+
return raw.slice(start, end);
|
|
12898
|
+
}
|
|
12899
|
+
function tokenize(text) {
|
|
12900
|
+
const out = [];
|
|
12901
|
+
for (const raw of text.split(/\s+/)) {
|
|
12902
|
+
const token = trimTokenPunctuation(raw);
|
|
12903
|
+
if (token.length > 0) {
|
|
12904
|
+
out.push(token);
|
|
12905
|
+
}
|
|
12906
|
+
}
|
|
12907
|
+
return out;
|
|
12908
|
+
}
|
|
12909
|
+
function isNxBinary(token) {
|
|
12910
|
+
return token === "nx" || token.endsWith("/nx");
|
|
12911
|
+
}
|
|
12912
|
+
function changesToForeignDirectory(tokens) {
|
|
12913
|
+
let result = false;
|
|
12914
|
+
for (let i = 0; i < tokens.length - 1 && !result; i += 1) {
|
|
12915
|
+
if (tokens[i] === "cd") {
|
|
12916
|
+
const destination = tokens[i + 1];
|
|
12917
|
+
result = destination.startsWith("~") || destination.startsWith("/");
|
|
12918
|
+
}
|
|
12919
|
+
}
|
|
12920
|
+
return result;
|
|
12921
|
+
}
|
|
12922
|
+
function parseExplicitForm(tokens, runIndex) {
|
|
12923
|
+
let result;
|
|
12924
|
+
for (let i = runIndex + 1; i < tokens.length; i += 1) {
|
|
12925
|
+
const token = tokens[i];
|
|
12926
|
+
if (isFlag(token)) {
|
|
12927
|
+
continue;
|
|
12928
|
+
}
|
|
12929
|
+
if (isResolvable(token) && token.includes(":")) {
|
|
12930
|
+
const [project, target, configuration] = token.split(":");
|
|
12931
|
+
if (project && target && PROJECT_TOKEN_PATTERN.test(project) && TARGET_TOKEN_PATTERN.test(target)) {
|
|
12932
|
+
result = { project, target, configuration: configuration || void 0, raw: token };
|
|
12933
|
+
}
|
|
12934
|
+
}
|
|
12935
|
+
break;
|
|
12936
|
+
}
|
|
12937
|
+
return result;
|
|
12938
|
+
}
|
|
12939
|
+
function parseShorthandForm(tokens, nxIndex) {
|
|
12940
|
+
const positional = [];
|
|
12941
|
+
for (let i = nxIndex + 1; i < tokens.length && positional.length < 2; i += 1) {
|
|
12942
|
+
const token = tokens[i];
|
|
12943
|
+
if (!isFlag(token)) {
|
|
12944
|
+
positional.push(token);
|
|
12945
|
+
}
|
|
12946
|
+
}
|
|
12947
|
+
let result;
|
|
12948
|
+
const [target, project] = positional;
|
|
12949
|
+
if (target !== void 0 && project !== void 0 && !NX_SUBCOMMANDS.has(target) && !target.includes(":") && isResolvable(target) && isResolvable(project) && TARGET_TOKEN_PATTERN.test(target) && PROJECT_TOKEN_PATTERN.test(project)) {
|
|
12950
|
+
result = { project, target, raw: `${target} ${project}` };
|
|
12951
|
+
}
|
|
12952
|
+
return result;
|
|
12953
|
+
}
|
|
12954
|
+
function scanTargetReferences(input) {
|
|
12955
|
+
const { text, sourceFile, sourceTarget, line } = input;
|
|
12956
|
+
const references = [];
|
|
12957
|
+
const tokens = tokenize(text);
|
|
12958
|
+
if (changesToForeignDirectory(tokens)) {
|
|
12959
|
+
return references;
|
|
12960
|
+
}
|
|
12961
|
+
for (let i = 0; i < tokens.length; i += 1) {
|
|
12962
|
+
if (!isNxBinary(tokens[i])) {
|
|
12963
|
+
continue;
|
|
12964
|
+
}
|
|
12965
|
+
let firstPositional;
|
|
12966
|
+
let firstPositionalIndex = -1;
|
|
12967
|
+
for (let j = i + 1; j < tokens.length; j += 1) {
|
|
12968
|
+
if (!isFlag(tokens[j])) {
|
|
12969
|
+
firstPositional = tokens[j];
|
|
12970
|
+
firstPositionalIndex = j;
|
|
12971
|
+
break;
|
|
12972
|
+
}
|
|
12973
|
+
}
|
|
12974
|
+
if (firstPositional === void 0) {
|
|
12975
|
+
continue;
|
|
12976
|
+
}
|
|
12977
|
+
if (firstPositional === "run") {
|
|
12978
|
+
const parsed = parseExplicitForm(tokens, firstPositionalIndex);
|
|
12979
|
+
if (parsed) {
|
|
12980
|
+
references.push({ project: parsed.project, target: parsed.target, configuration: parsed.configuration, sourceFile, line, sourceTarget, raw: parsed.raw });
|
|
12981
|
+
}
|
|
12982
|
+
} else if (!NX_SUBCOMMANDS.has(firstPositional)) {
|
|
12983
|
+
const parsed = parseShorthandForm(tokens, i);
|
|
12984
|
+
if (parsed) {
|
|
12985
|
+
references.push({ project: parsed.project, target: parsed.target, configuration: void 0, sourceFile, line, sourceTarget, raw: parsed.raw });
|
|
12986
|
+
}
|
|
12987
|
+
}
|
|
12988
|
+
}
|
|
12989
|
+
return references;
|
|
12990
|
+
}
|
|
12991
|
+
function stripComment(line) {
|
|
12992
|
+
const trimmed = line.trimStart();
|
|
12993
|
+
let result;
|
|
12994
|
+
if (trimmed.startsWith("#")) {
|
|
12995
|
+
result = "";
|
|
12996
|
+
} else {
|
|
12997
|
+
const index = line.indexOf(" #");
|
|
12998
|
+
result = index === -1 ? line : line.slice(0, index);
|
|
12999
|
+
}
|
|
13000
|
+
return result;
|
|
13001
|
+
}
|
|
13002
|
+
function scanDocumentTargetReferences(input) {
|
|
13003
|
+
const { text, sourceFile } = input;
|
|
13004
|
+
const references = [];
|
|
13005
|
+
const lines = text.split("\n");
|
|
13006
|
+
for (const [index, line] of lines.entries()) {
|
|
13007
|
+
for (const reference of scanTargetReferences({ text: stripComment(line), sourceFile, line: index + 1 })) {
|
|
13008
|
+
references.push(reference);
|
|
13009
|
+
}
|
|
13010
|
+
}
|
|
13011
|
+
return references;
|
|
13012
|
+
}
|
|
13013
|
+
function scanHostingTargets(command) {
|
|
13014
|
+
const out = [];
|
|
13015
|
+
const pattern = /hosting:([a-zA-Z0-9_-]+)/g;
|
|
13016
|
+
let match = pattern.exec(command);
|
|
13017
|
+
while (match !== null) {
|
|
13018
|
+
out.push(match[1]);
|
|
13019
|
+
match = pattern.exec(command);
|
|
13020
|
+
}
|
|
13021
|
+
return out;
|
|
13022
|
+
}
|
|
13023
|
+
|
|
13024
|
+
// packages/dbx-cli/validate/src/lib/workspace-validate/inspect.ts
|
|
13025
|
+
var SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set(["node_modules", "dist", "tmp", "coverage", ".nx", ".git", ".firebase", ".angular", ".cache", "templates"]);
|
|
13026
|
+
var MAX_WALK_DEPTH = 4;
|
|
13027
|
+
var PROJECT_CONFIG_FILENAME = "project.json";
|
|
13028
|
+
var ENVIRONMENTS_SUBPATH = join10("src", "environments");
|
|
13029
|
+
var ENVIRONMENT_FILE_PATTERN = /^environment\..+\.ts$/;
|
|
13030
|
+
var EXTRA_REFERENCE_SOURCES = [".circleci/config.yml", ".circleci/config.yaml", ".github/workflows"];
|
|
13031
|
+
async function pathExists(path) {
|
|
13032
|
+
let result;
|
|
13033
|
+
try {
|
|
13034
|
+
await stat10(path);
|
|
13035
|
+
result = true;
|
|
13036
|
+
} catch {
|
|
13037
|
+
result = false;
|
|
13038
|
+
}
|
|
13039
|
+
return result;
|
|
13040
|
+
}
|
|
13041
|
+
async function readJsonFile(path) {
|
|
13042
|
+
let result;
|
|
13043
|
+
try {
|
|
13044
|
+
const text = await readFile11(path, "utf8");
|
|
13045
|
+
const parsed = JSON.parse(text);
|
|
13046
|
+
result = parsed !== null && typeof parsed === "object" ? parsed : void 0;
|
|
13047
|
+
} catch {
|
|
13048
|
+
result = void 0;
|
|
13049
|
+
}
|
|
13050
|
+
return result;
|
|
13051
|
+
}
|
|
13052
|
+
async function collectProjectConfigPaths(config) {
|
|
13053
|
+
const { workspaceRoot, relativeDir, depth } = config;
|
|
13054
|
+
const collected = [];
|
|
13055
|
+
const absoluteDir = join10(workspaceRoot, relativeDir);
|
|
13056
|
+
const entries = await readdir8(absoluteDir, { withFileTypes: true }).catch(() => []);
|
|
13057
|
+
for (const entry of entries) {
|
|
13058
|
+
if (entry.isFile() && entry.name === PROJECT_CONFIG_FILENAME) {
|
|
13059
|
+
collected.push(relativeDir === "" ? PROJECT_CONFIG_FILENAME : join10(relativeDir, PROJECT_CONFIG_FILENAME));
|
|
13060
|
+
} else if (entry.isDirectory() && depth < MAX_WALK_DEPTH && !SKIPPED_DIRECTORIES.has(entry.name) && !entry.name.startsWith("firebase-export-")) {
|
|
13061
|
+
const nested = await collectProjectConfigPaths({ workspaceRoot, relativeDir: relativeDir === "" ? entry.name : join10(relativeDir, entry.name), depth: depth + 1 });
|
|
13062
|
+
for (const path of nested) {
|
|
13063
|
+
collected.push(path);
|
|
13064
|
+
}
|
|
13065
|
+
}
|
|
13066
|
+
}
|
|
13067
|
+
return collected;
|
|
13068
|
+
}
|
|
13069
|
+
function extractCommands(options) {
|
|
13070
|
+
const collected = [];
|
|
13071
|
+
const single = options["command"];
|
|
13072
|
+
if (typeof single === "string") {
|
|
13073
|
+
collected.push(single);
|
|
13074
|
+
}
|
|
13075
|
+
const many = options["commands"];
|
|
13076
|
+
if (Array.isArray(many)) {
|
|
13077
|
+
for (const entry of many) {
|
|
13078
|
+
if (typeof entry === "string") {
|
|
13079
|
+
collected.push(entry);
|
|
13080
|
+
} else if (entry !== null && typeof entry === "object") {
|
|
13081
|
+
const command = entry["command"];
|
|
13082
|
+
if (typeof command === "string") {
|
|
13083
|
+
collected.push(command);
|
|
13084
|
+
}
|
|
13085
|
+
}
|
|
13086
|
+
}
|
|
13087
|
+
}
|
|
13088
|
+
return collected;
|
|
13089
|
+
}
|
|
13090
|
+
async function resolveFileReplacements(config) {
|
|
13091
|
+
const { workspaceRoot, raw } = config;
|
|
13092
|
+
const collected = [];
|
|
13093
|
+
if (Array.isArray(raw)) {
|
|
13094
|
+
for (const entry of raw) {
|
|
13095
|
+
if (entry === null || typeof entry !== "object") {
|
|
13096
|
+
continue;
|
|
13097
|
+
}
|
|
13098
|
+
const record = entry;
|
|
13099
|
+
const replace = record["replace"];
|
|
13100
|
+
const withPath = record["with"];
|
|
13101
|
+
if (typeof replace === "string" && typeof withPath === "string") {
|
|
13102
|
+
collected.push({
|
|
13103
|
+
replace,
|
|
13104
|
+
with: withPath,
|
|
13105
|
+
replaceExists: await pathExists(join10(workspaceRoot, replace)),
|
|
13106
|
+
withExists: await pathExists(join10(workspaceRoot, withPath))
|
|
13107
|
+
});
|
|
13108
|
+
}
|
|
13109
|
+
}
|
|
13110
|
+
}
|
|
13111
|
+
return collected;
|
|
13112
|
+
}
|
|
13113
|
+
async function resolveConfigurations(config) {
|
|
13114
|
+
const { workspaceRoot, raw } = config;
|
|
13115
|
+
const collected = [];
|
|
13116
|
+
if (raw !== null && typeof raw === "object") {
|
|
13117
|
+
for (const [name, value] of Object.entries(raw)) {
|
|
13118
|
+
const options = value !== null && typeof value === "object" ? value : {};
|
|
13119
|
+
const esbuildConfigValue = options["esbuildConfig"];
|
|
13120
|
+
const esbuildConfig = typeof esbuildConfigValue === "string" ? esbuildConfigValue : void 0;
|
|
13121
|
+
collected.push({
|
|
13122
|
+
name,
|
|
13123
|
+
fileReplacements: await resolveFileReplacements({ workspaceRoot, raw: options["fileReplacements"] }),
|
|
13124
|
+
esbuildConfig,
|
|
13125
|
+
esbuildConfigExists: esbuildConfig === void 0 ? false : await pathExists(join10(workspaceRoot, esbuildConfig))
|
|
13126
|
+
});
|
|
13127
|
+
}
|
|
13128
|
+
}
|
|
13129
|
+
return collected;
|
|
13130
|
+
}
|
|
13131
|
+
async function resolveTargets(config) {
|
|
13132
|
+
const { workspaceRoot, raw } = config;
|
|
13133
|
+
const collected = [];
|
|
13134
|
+
if (raw !== null && typeof raw === "object") {
|
|
13135
|
+
for (const [name, value] of Object.entries(raw)) {
|
|
13136
|
+
const target = value !== null && typeof value === "object" ? value : {};
|
|
13137
|
+
const executorValue = target["executor"];
|
|
13138
|
+
const defaultConfigurationValue = target["defaultConfiguration"];
|
|
13139
|
+
const optionsValue = target["options"];
|
|
13140
|
+
const options = optionsValue !== null && typeof optionsValue === "object" ? optionsValue : {};
|
|
13141
|
+
const outputsValue = target["outputs"];
|
|
13142
|
+
collected.push({
|
|
13143
|
+
name,
|
|
13144
|
+
executor: typeof executorValue === "string" ? executorValue : void 0,
|
|
13145
|
+
configurations: await resolveConfigurations({ workspaceRoot, raw: target["configurations"] }),
|
|
13146
|
+
defaultConfiguration: typeof defaultConfigurationValue === "string" ? defaultConfigurationValue : void 0,
|
|
13147
|
+
outputs: Array.isArray(outputsValue) ? outputsValue.filter((o) => typeof o === "string") : [],
|
|
13148
|
+
options,
|
|
13149
|
+
commands: extractCommands(options)
|
|
13150
|
+
});
|
|
13151
|
+
}
|
|
13152
|
+
}
|
|
13153
|
+
return collected;
|
|
13154
|
+
}
|
|
13155
|
+
async function collectEnvironmentFiles(config) {
|
|
13156
|
+
const { workspaceRoot, projectRoot } = config;
|
|
13157
|
+
const relativeDir = projectRoot === "" ? ENVIRONMENTS_SUBPATH : join10(projectRoot, ENVIRONMENTS_SUBPATH);
|
|
13158
|
+
const collected = [];
|
|
13159
|
+
const entries = await readdir8(join10(workspaceRoot, relativeDir), { withFileTypes: true }).catch(() => []);
|
|
13160
|
+
for (const entry of entries) {
|
|
13161
|
+
if (entry.isFile() && ENVIRONMENT_FILE_PATTERN.test(entry.name)) {
|
|
13162
|
+
collected.push(join10(relativeDir, entry.name));
|
|
13163
|
+
}
|
|
13164
|
+
}
|
|
13165
|
+
return collected.sort((a, b) => a.localeCompare(b));
|
|
13166
|
+
}
|
|
13167
|
+
var ENVIRONMENT_MENTION_PATTERN = /environment(?:\.[A-Za-z0-9_-]+)*\.ts/g;
|
|
13168
|
+
async function collectReferencedEnvironmentFiles(config) {
|
|
13169
|
+
const { workspaceRoot, targets, environmentFiles } = config;
|
|
13170
|
+
const referenced = /* @__PURE__ */ new Set();
|
|
13171
|
+
const esbuildConfigPaths = /* @__PURE__ */ new Set();
|
|
13172
|
+
for (const target of targets) {
|
|
13173
|
+
const baseEsbuildConfig = target.options["esbuildConfig"];
|
|
13174
|
+
if (typeof baseEsbuildConfig === "string") {
|
|
13175
|
+
esbuildConfigPaths.add(baseEsbuildConfig);
|
|
13176
|
+
}
|
|
13177
|
+
for (const configuration of target.configurations) {
|
|
13178
|
+
for (const replacement of configuration.fileReplacements) {
|
|
13179
|
+
referenced.add(replacement.with);
|
|
13180
|
+
}
|
|
13181
|
+
if (configuration.esbuildConfig !== void 0) {
|
|
13182
|
+
esbuildConfigPaths.add(configuration.esbuildConfig);
|
|
13183
|
+
}
|
|
13184
|
+
}
|
|
13185
|
+
}
|
|
13186
|
+
const mentionedBasenames = /* @__PURE__ */ new Set();
|
|
13187
|
+
for (const configPath of esbuildConfigPaths) {
|
|
13188
|
+
const text = await readFile11(join10(workspaceRoot, configPath), "utf8").catch(() => void 0);
|
|
13189
|
+
if (text === void 0) {
|
|
13190
|
+
continue;
|
|
13191
|
+
}
|
|
13192
|
+
for (const match of text.matchAll(ENVIRONMENT_MENTION_PATTERN)) {
|
|
13193
|
+
mentionedBasenames.add(match[0]);
|
|
13194
|
+
}
|
|
13195
|
+
}
|
|
13196
|
+
for (const environmentFile of environmentFiles) {
|
|
13197
|
+
if (mentionedBasenames.has(basename5(environmentFile))) {
|
|
13198
|
+
referenced.add(environmentFile);
|
|
13199
|
+
}
|
|
13200
|
+
}
|
|
13201
|
+
return Array.from(referenced).sort((a, b) => a.localeCompare(b));
|
|
13202
|
+
}
|
|
13203
|
+
async function inspectProject(config) {
|
|
13204
|
+
const { workspaceRoot, configFile } = config;
|
|
13205
|
+
const parsed = await readJsonFile(join10(workspaceRoot, configFile));
|
|
13206
|
+
if (parsed === void 0) {
|
|
13207
|
+
return void 0;
|
|
13208
|
+
}
|
|
13209
|
+
const projectRoot = dirname2(configFile) === "." ? "" : dirname2(configFile);
|
|
13210
|
+
const nameValue = parsed["name"];
|
|
13211
|
+
const fallbackNameDir = projectRoot === "" ? workspaceRoot : projectRoot;
|
|
13212
|
+
const name = typeof nameValue === "string" && nameValue.length > 0 ? nameValue : basename5(fallbackNameDir);
|
|
13213
|
+
const targets = await resolveTargets({ workspaceRoot, raw: parsed["targets"] });
|
|
13214
|
+
const environmentFiles = await collectEnvironmentFiles({ workspaceRoot, projectRoot });
|
|
13215
|
+
return {
|
|
13216
|
+
name,
|
|
13217
|
+
root: projectRoot,
|
|
13218
|
+
configFile,
|
|
13219
|
+
targets,
|
|
13220
|
+
environmentFiles,
|
|
13221
|
+
referencedEnvironmentFiles: await collectReferencedEnvironmentFiles({ workspaceRoot, targets, environmentFiles })
|
|
13222
|
+
};
|
|
13223
|
+
}
|
|
13224
|
+
async function inspectNxConfig(workspaceRoot) {
|
|
13225
|
+
const parsed = await readJsonFile(join10(workspaceRoot, "nx.json"));
|
|
13226
|
+
const targetDefaultNames = [];
|
|
13227
|
+
const plugins = [];
|
|
13228
|
+
const targetDefaults = parsed?.["targetDefaults"];
|
|
13229
|
+
if (targetDefaults !== null && typeof targetDefaults === "object") {
|
|
13230
|
+
for (const key of Object.keys(targetDefaults)) {
|
|
13231
|
+
if (!key.includes(":")) {
|
|
13232
|
+
targetDefaultNames.push(key);
|
|
13233
|
+
}
|
|
13234
|
+
}
|
|
13235
|
+
}
|
|
13236
|
+
const pluginsValue = parsed?.["plugins"];
|
|
13237
|
+
if (Array.isArray(pluginsValue)) {
|
|
13238
|
+
for (const entry of pluginsValue) {
|
|
13239
|
+
if (typeof entry === "string") {
|
|
13240
|
+
plugins.push(entry);
|
|
13241
|
+
} else if (entry !== null && typeof entry === "object") {
|
|
13242
|
+
const plugin = entry["plugin"];
|
|
13243
|
+
if (typeof plugin === "string") {
|
|
13244
|
+
plugins.push(plugin);
|
|
13245
|
+
}
|
|
13246
|
+
}
|
|
13247
|
+
}
|
|
13248
|
+
}
|
|
13249
|
+
return { targetDefaultNames, plugins };
|
|
13250
|
+
}
|
|
13251
|
+
async function inspectFirebaseConfig(workspaceRoot) {
|
|
13252
|
+
const parsed = await readJsonFile(join10(workspaceRoot, "firebase.json"));
|
|
13253
|
+
if (parsed === void 0) {
|
|
13254
|
+
return { present: false, hostingTargets: [] };
|
|
13255
|
+
}
|
|
13256
|
+
const hostingValue = parsed["hosting"];
|
|
13257
|
+
const singleHostingEntry = hostingValue !== null && typeof hostingValue === "object" ? [hostingValue] : [];
|
|
13258
|
+
const entries = Array.isArray(hostingValue) ? hostingValue : singleHostingEntry;
|
|
13259
|
+
const hostingTargets = [];
|
|
13260
|
+
for (const entry of entries) {
|
|
13261
|
+
if (entry === null || typeof entry !== "object") {
|
|
13262
|
+
continue;
|
|
13263
|
+
}
|
|
13264
|
+
const record = entry;
|
|
13265
|
+
const target = record["target"];
|
|
13266
|
+
const site = record["site"];
|
|
13267
|
+
if (typeof target === "string") {
|
|
13268
|
+
hostingTargets.push(target);
|
|
13269
|
+
} else if (typeof site === "string") {
|
|
13270
|
+
hostingTargets.push(site);
|
|
13271
|
+
}
|
|
13272
|
+
}
|
|
13273
|
+
return { present: true, hostingTargets };
|
|
13274
|
+
}
|
|
13275
|
+
async function collectExtraReferenceSources(workspaceRoot) {
|
|
13276
|
+
const collected = [];
|
|
13277
|
+
for (const candidate of EXTRA_REFERENCE_SOURCES) {
|
|
13278
|
+
const absolute = join10(workspaceRoot, candidate);
|
|
13279
|
+
let stats;
|
|
13280
|
+
try {
|
|
13281
|
+
stats = await stat10(absolute);
|
|
13282
|
+
} catch {
|
|
13283
|
+
continue;
|
|
13284
|
+
}
|
|
13285
|
+
if (stats.isFile()) {
|
|
13286
|
+
collected.push(candidate);
|
|
13287
|
+
} else if (stats.isDirectory()) {
|
|
13288
|
+
const entries = await readdir8(absolute, { withFileTypes: true });
|
|
13289
|
+
for (const entry of entries) {
|
|
13290
|
+
if (entry.isFile() && /\.ya?ml$/.test(entry.name)) {
|
|
13291
|
+
collected.push(join10(candidate, entry.name));
|
|
13292
|
+
}
|
|
13293
|
+
}
|
|
13294
|
+
}
|
|
13295
|
+
}
|
|
13296
|
+
const rootEntries = await readdir8(workspaceRoot, { withFileTypes: true }).catch(() => []);
|
|
13297
|
+
for (const entry of rootEntries) {
|
|
13298
|
+
if (entry.isFile() && entry.name.endsWith(".sh")) {
|
|
13299
|
+
collected.push(entry.name);
|
|
13300
|
+
}
|
|
13301
|
+
}
|
|
13302
|
+
return collected.sort((a, b) => a.localeCompare(b));
|
|
13303
|
+
}
|
|
13304
|
+
async function collectPackageScriptReferences(workspaceRoot) {
|
|
13305
|
+
const parsed = await readJsonFile(join10(workspaceRoot, "package.json"));
|
|
13306
|
+
const scripts = parsed?.["scripts"];
|
|
13307
|
+
const collected = [];
|
|
13308
|
+
if (scripts !== null && typeof scripts === "object") {
|
|
13309
|
+
for (const [scriptName, value] of Object.entries(scripts)) {
|
|
13310
|
+
if (typeof value === "string") {
|
|
13311
|
+
for (const reference of scanTargetReferences({ text: value, sourceFile: "package.json", sourceTarget: scriptName })) {
|
|
13312
|
+
collected.push(reference);
|
|
13313
|
+
}
|
|
13314
|
+
}
|
|
13315
|
+
}
|
|
13316
|
+
}
|
|
13317
|
+
return collected;
|
|
13318
|
+
}
|
|
13319
|
+
async function inspectWorkspace(input) {
|
|
13320
|
+
const workspaceRoot = resolve3(input.workspaceRoot);
|
|
13321
|
+
const configPaths = await collectProjectConfigPaths({ workspaceRoot, relativeDir: "", depth: 0 });
|
|
13322
|
+
const projects = [];
|
|
13323
|
+
const references = [];
|
|
13324
|
+
for (const configFile of [...configPaths].sort((a, b) => a.localeCompare(b))) {
|
|
13325
|
+
const project = await inspectProject({ workspaceRoot, configFile });
|
|
13326
|
+
if (project === void 0) {
|
|
13327
|
+
continue;
|
|
13328
|
+
}
|
|
13329
|
+
projects.push(project);
|
|
13330
|
+
for (const target of project.targets) {
|
|
13331
|
+
for (const command of target.commands) {
|
|
13332
|
+
for (const reference of scanTargetReferences({ text: command, sourceFile: configFile, sourceTarget: target.name })) {
|
|
13333
|
+
references.push(reference);
|
|
13334
|
+
}
|
|
13335
|
+
}
|
|
13336
|
+
}
|
|
13337
|
+
}
|
|
13338
|
+
for (const sourceFile of await collectExtraReferenceSources(workspaceRoot)) {
|
|
13339
|
+
const text = await readFile11(join10(workspaceRoot, sourceFile), "utf8").catch(() => void 0);
|
|
13340
|
+
if (text !== void 0) {
|
|
13341
|
+
for (const reference of scanDocumentTargetReferences({ text, sourceFile })) {
|
|
13342
|
+
references.push(reference);
|
|
13343
|
+
}
|
|
13344
|
+
}
|
|
13345
|
+
}
|
|
13346
|
+
for (const reference of await collectPackageScriptReferences(workspaceRoot)) {
|
|
13347
|
+
references.push(reference);
|
|
13348
|
+
}
|
|
13349
|
+
return {
|
|
13350
|
+
workspaceRoot,
|
|
13351
|
+
projects,
|
|
13352
|
+
references,
|
|
13353
|
+
firebase: await inspectFirebaseConfig(workspaceRoot),
|
|
13354
|
+
nx: await inspectNxConfig(workspaceRoot)
|
|
13355
|
+
};
|
|
13356
|
+
}
|
|
13357
|
+
function pathEscapesWorkspaceRoot(config) {
|
|
13358
|
+
const { workspaceRoot, candidate } = config;
|
|
13359
|
+
const resolved = resolve3(workspaceRoot, candidate);
|
|
13360
|
+
const rel = relative6(workspaceRoot, resolved);
|
|
13361
|
+
return rel.startsWith("..");
|
|
13362
|
+
}
|
|
13363
|
+
|
|
13364
|
+
// packages/dbx-cli/validate/src/lib/workspace-validate/types.ts
|
|
13365
|
+
var WORKSPACE_RULE_GROUPS = ["targets", "deploy"];
|
|
13366
|
+
var DEPLOY_TARGET_PREFIX = "ci-deploy-";
|
|
13367
|
+
|
|
13368
|
+
// packages/dbx-cli/validate/src/lib/workspace-validate/rules.ts
|
|
13369
|
+
var FILE_REPLACEMENT_EXECUTORS = /* @__PURE__ */ new Set(["@nx/angular:application", "@nx/angular:browser-esbuild", "@angular/build:application", "@angular-devkit/build-angular:application", "@angular-devkit/build-angular:browser"]);
|
|
13370
|
+
var ESBUILD_CONFIG_EXECUTORS = /* @__PURE__ */ new Set(["@nx/esbuild:esbuild"]);
|
|
13371
|
+
var PLUGIN_INFERRED_TARGETS = [
|
|
13372
|
+
{ plugin: "@nx/eslint/plugin", targets: ["lint"] },
|
|
13373
|
+
{ plugin: "@nx/vitest", targets: ["test", "test-ci"] },
|
|
13374
|
+
{ plugin: "@nx/vite", targets: ["build", "serve", "preview", "test"] },
|
|
13375
|
+
{ plugin: "@nx/jest", targets: ["test"] },
|
|
13376
|
+
{ plugin: "@nx/webpack/plugin", targets: ["build", "serve", "preview"] },
|
|
13377
|
+
{ plugin: "@nx/rollup/plugin", targets: ["build"] },
|
|
13378
|
+
{ plugin: "@nx/js/typescript", targets: ["typecheck", "build"] },
|
|
13379
|
+
{ plugin: "@nx/playwright/plugin", targets: ["e2e", "e2e-ci"] },
|
|
13380
|
+
{ plugin: "@nx/cypress/plugin", targets: ["e2e", "component-test"] },
|
|
13381
|
+
{ plugin: "@nx/next/plugin", targets: ["build", "dev", "start", "serve-static"] }
|
|
13382
|
+
];
|
|
13383
|
+
var FILE_EXTENSION_PATTERN = /\.(?:ts|tsx|js|mjs|cjs|json|sh|ya?ml|md|html|scss|css)$/;
|
|
13384
|
+
var DEPLOY_LANE_PATTERN = new RegExp(`^${DEPLOY_TARGET_PREFIX}([a-z0-9]+)$`);
|
|
13385
|
+
function pushViolation11(buffer, violation) {
|
|
13386
|
+
const severity = violation.severity ?? "error";
|
|
13387
|
+
const filled = {
|
|
13388
|
+
code: violation.code,
|
|
13389
|
+
severity,
|
|
13390
|
+
message: violation.message,
|
|
13391
|
+
group: violation.group,
|
|
13392
|
+
project: violation.project,
|
|
13393
|
+
file: violation.file,
|
|
13394
|
+
line: violation.line,
|
|
13395
|
+
remediation: attachRemediation(violation.code)
|
|
13396
|
+
};
|
|
13397
|
+
buffer.push(filled);
|
|
13398
|
+
}
|
|
13399
|
+
function looksLikeProjectName(token) {
|
|
13400
|
+
return !token.includes("/") && !token.includes("\\") && !FILE_EXTENSION_PATTERN.test(token);
|
|
13401
|
+
}
|
|
13402
|
+
function isExemptTargetName(config) {
|
|
13403
|
+
const { targetName, nx } = config;
|
|
13404
|
+
let result = nx.targetDefaultNames.includes(targetName);
|
|
13405
|
+
if (!result) {
|
|
13406
|
+
const plugins = new Set(nx.plugins);
|
|
13407
|
+
result = PLUGIN_INFERRED_TARGETS.some((entry) => plugins.has(entry.plugin) && entry.targets.includes(targetName));
|
|
13408
|
+
}
|
|
13409
|
+
return result;
|
|
13410
|
+
}
|
|
13411
|
+
function substituteOutputTokens(config) {
|
|
13412
|
+
const { entry, options, projectRoot } = config;
|
|
13413
|
+
let unresolved = false;
|
|
13414
|
+
const withOptions = entry.replaceAll(/\{options\.(\w+)\}/g, (_match, key) => {
|
|
13415
|
+
const value = options[key];
|
|
13416
|
+
let replacement;
|
|
13417
|
+
if (typeof value === "string") {
|
|
13418
|
+
replacement = value;
|
|
13419
|
+
} else if (typeof value === "number" || typeof value === "boolean") {
|
|
13420
|
+
replacement = String(value);
|
|
13421
|
+
} else {
|
|
13422
|
+
unresolved = true;
|
|
13423
|
+
replacement = "";
|
|
13424
|
+
}
|
|
13425
|
+
return replacement;
|
|
13426
|
+
});
|
|
13427
|
+
const projectRootValue = projectRoot === "" ? "." : projectRoot;
|
|
13428
|
+
return unresolved ? void 0 : withOptions.replaceAll("{workspaceRoot}", ".").replaceAll("{projectRoot}", projectRootValue);
|
|
13429
|
+
}
|
|
13430
|
+
function environmentSelectionState(config) {
|
|
13431
|
+
const { executor, configuration } = config;
|
|
13432
|
+
let result;
|
|
13433
|
+
if (executor !== void 0 && FILE_REPLACEMENT_EXECUTORS.has(executor)) {
|
|
13434
|
+
result = configuration.fileReplacements.length > 0 ? "ok" : "missing";
|
|
13435
|
+
} else if (executor !== void 0 && ESBUILD_CONFIG_EXECUTORS.has(executor)) {
|
|
13436
|
+
result = configuration.esbuildConfig === void 0 ? "missing" : "ok";
|
|
13437
|
+
} else {
|
|
13438
|
+
result = "unknown";
|
|
13439
|
+
}
|
|
13440
|
+
return result;
|
|
13441
|
+
}
|
|
13442
|
+
function environmentSelectionOptionName(executor) {
|
|
13443
|
+
return executor !== void 0 && ESBUILD_CONFIG_EXECUTORS.has(executor) ? "esbuildConfig" : "fileReplacements";
|
|
13444
|
+
}
|
|
13445
|
+
function indexTargets(project) {
|
|
13446
|
+
const map = /* @__PURE__ */ new Map();
|
|
13447
|
+
for (const target of project.targets) {
|
|
13448
|
+
map.set(target.name, target);
|
|
13449
|
+
}
|
|
13450
|
+
return map;
|
|
13451
|
+
}
|
|
13452
|
+
function checkTargetReferences(config) {
|
|
13453
|
+
const { inspection, targetsByProject, violations } = config;
|
|
13454
|
+
const group = "targets";
|
|
13455
|
+
for (const reference of inspection.references) {
|
|
13456
|
+
const targets = targetsByProject.get(reference.project);
|
|
13457
|
+
if (targets === void 0) {
|
|
13458
|
+
if (looksLikeProjectName(reference.project)) {
|
|
13459
|
+
pushViolation11(violations, {
|
|
13460
|
+
code: "WORKSPACE_TARGET_REF_PROJECT_MISSING",
|
|
13461
|
+
message: `\`${reference.raw}\` references project \`${reference.project}\`, which the workspace does not declare.`,
|
|
13462
|
+
group,
|
|
13463
|
+
project: reference.project,
|
|
13464
|
+
file: reference.sourceFile,
|
|
13465
|
+
line: reference.line
|
|
13466
|
+
});
|
|
13467
|
+
}
|
|
13468
|
+
continue;
|
|
13469
|
+
}
|
|
13470
|
+
const target = targets.get(reference.target);
|
|
13471
|
+
if (target === void 0) {
|
|
13472
|
+
if (!isExemptTargetName({ targetName: reference.target, nx: inspection.nx })) {
|
|
13473
|
+
pushViolation11(violations, {
|
|
13474
|
+
code: "WORKSPACE_TARGET_REF_TARGET_MISSING",
|
|
13475
|
+
message: `\`${reference.raw}\` references target \`${reference.target}\` on project \`${reference.project}\`, which declares no such target.`,
|
|
13476
|
+
group,
|
|
13477
|
+
project: reference.project,
|
|
13478
|
+
file: reference.sourceFile,
|
|
13479
|
+
line: reference.line
|
|
13480
|
+
});
|
|
13481
|
+
}
|
|
13482
|
+
continue;
|
|
13483
|
+
}
|
|
13484
|
+
if (reference.configuration !== void 0 && target.configurations.length > 0 && !target.configurations.some((configuration) => configuration.name === reference.configuration)) {
|
|
13485
|
+
const declared = target.configurations.map((configuration) => configuration.name).join(", ");
|
|
13486
|
+
const fallback = target.defaultConfiguration === void 0 ? "no configuration at all \u2014 the target runs with its base options, so every configuration-only option is silently dropped" : `\`${target.defaultConfiguration}\` (this target's \`defaultConfiguration\`)`;
|
|
13487
|
+
pushViolation11(violations, {
|
|
13488
|
+
code: "WORKSPACE_TARGET_REF_CONFIGURATION_MISSING",
|
|
13489
|
+
message: `\`${reference.raw}\` requests configuration \`${reference.configuration}\` of \`${reference.project}:${reference.target}\`, which declares only [${declared}]. Nx does not error here \u2014 it silently builds ${fallback}.`,
|
|
13490
|
+
group,
|
|
13491
|
+
project: reference.project,
|
|
13492
|
+
file: reference.sourceFile,
|
|
13493
|
+
line: reference.line
|
|
13494
|
+
});
|
|
13495
|
+
}
|
|
13496
|
+
}
|
|
13497
|
+
}
|
|
13498
|
+
function checkTargetOutputs(config) {
|
|
13499
|
+
const { inspection, violations } = config;
|
|
13500
|
+
const group = "targets";
|
|
13501
|
+
for (const project of inspection.projects) {
|
|
13502
|
+
for (const target of project.targets) {
|
|
13503
|
+
for (const entry of target.outputs) {
|
|
13504
|
+
const substituted = substituteOutputTokens({ entry, options: target.options, projectRoot: project.root });
|
|
13505
|
+
if (substituted === void 0) {
|
|
13506
|
+
continue;
|
|
13507
|
+
}
|
|
13508
|
+
if (pathEscapesWorkspaceRoot({ workspaceRoot: inspection.workspaceRoot, candidate: substituted })) {
|
|
13509
|
+
pushViolation11(violations, {
|
|
13510
|
+
code: "WORKSPACE_TARGET_OUTPUT_ESCAPES_ROOT",
|
|
13511
|
+
message: `Target \`${project.name}:${target.name}\` declares output \`${entry}\`, which resolves to \`${substituted}\` \u2014 outside the workspace root. Nx cannot cache it and reports the task as flaky instead of failing.`,
|
|
13512
|
+
group,
|
|
13513
|
+
project: project.name,
|
|
13514
|
+
file: project.configFile,
|
|
13515
|
+
line: void 0
|
|
13516
|
+
});
|
|
13517
|
+
}
|
|
13518
|
+
}
|
|
13519
|
+
}
|
|
13520
|
+
}
|
|
13521
|
+
}
|
|
13522
|
+
function checkDeployLaneEnvironmentSelection(config) {
|
|
13523
|
+
const { inspection, targetsByProject, violations } = config;
|
|
13524
|
+
const group = "deploy";
|
|
13525
|
+
for (const project of inspection.projects) {
|
|
13526
|
+
const targets = targetsByProject.get(project.name);
|
|
13527
|
+
if (targets === void 0) {
|
|
13528
|
+
continue;
|
|
13529
|
+
}
|
|
13530
|
+
const deployTargetNames = new Set(project.targets.filter((target) => target.name.startsWith(DEPLOY_TARGET_PREFIX)).map((target) => target.name));
|
|
13531
|
+
for (const reference of inspection.references) {
|
|
13532
|
+
if (reference.sourceFile !== project.configFile || reference.sourceTarget === void 0 || !deployTargetNames.has(reference.sourceTarget) || reference.project !== project.name || reference.configuration === void 0) {
|
|
13533
|
+
continue;
|
|
13534
|
+
}
|
|
13535
|
+
const buildTarget = targets.get(reference.target);
|
|
13536
|
+
const configuration = buildTarget?.configurations.find((entry) => entry.name === reference.configuration);
|
|
13537
|
+
if (buildTarget === void 0 || configuration === void 0) {
|
|
13538
|
+
continue;
|
|
13539
|
+
}
|
|
13540
|
+
if (environmentSelectionState({ executor: buildTarget.executor, configuration }) === "missing") {
|
|
13541
|
+
pushViolation11(violations, {
|
|
13542
|
+
code: "WORKSPACE_DEPLOY_LANE_NO_ENVIRONMENT_SELECTION",
|
|
13543
|
+
message: `\`${project.name}:${reference.sourceTarget}\` builds \`${reference.target}:${configuration.name}\`, but that configuration declares no \`${environmentSelectionOptionName(buildTarget.executor)}\` \u2014 the lane deploys the unreplaced \`environment.ts\`.`,
|
|
13544
|
+
group,
|
|
13545
|
+
project: project.name,
|
|
13546
|
+
file: project.configFile,
|
|
13547
|
+
line: void 0
|
|
13548
|
+
});
|
|
13549
|
+
}
|
|
13550
|
+
}
|
|
13551
|
+
}
|
|
13552
|
+
}
|
|
13553
|
+
function checkConfigurationPaths(config) {
|
|
13554
|
+
const { inspection, violations } = config;
|
|
13555
|
+
const group = "deploy";
|
|
13556
|
+
for (const project of inspection.projects) {
|
|
13557
|
+
for (const target of project.targets) {
|
|
13558
|
+
for (const configuration of target.configurations) {
|
|
13559
|
+
for (const replacement of configuration.fileReplacements) {
|
|
13560
|
+
if (!replacement.replaceExists) {
|
|
13561
|
+
pushViolation11(violations, {
|
|
13562
|
+
code: "WORKSPACE_DEPLOY_ENVIRONMENT_FILE_MISSING",
|
|
13563
|
+
message: `\`${project.name}:${target.name}:${configuration.name}\` replaces \`${replacement.replace}\`, which does not exist.`,
|
|
13564
|
+
group,
|
|
13565
|
+
project: project.name,
|
|
13566
|
+
file: project.configFile,
|
|
13567
|
+
line: void 0
|
|
13568
|
+
});
|
|
13569
|
+
}
|
|
13570
|
+
if (!replacement.withExists) {
|
|
13571
|
+
pushViolation11(violations, {
|
|
13572
|
+
code: "WORKSPACE_DEPLOY_ENVIRONMENT_FILE_MISSING",
|
|
13573
|
+
message: `\`${project.name}:${target.name}:${configuration.name}\` replaces with \`${replacement.with}\`, which does not exist.`,
|
|
13574
|
+
group,
|
|
13575
|
+
project: project.name,
|
|
13576
|
+
file: project.configFile,
|
|
13577
|
+
line: void 0
|
|
13578
|
+
});
|
|
13579
|
+
}
|
|
13580
|
+
}
|
|
13581
|
+
if (configuration.esbuildConfig !== void 0 && !configuration.esbuildConfigExists) {
|
|
13582
|
+
pushViolation11(violations, {
|
|
13583
|
+
code: "WORKSPACE_DEPLOY_ENVIRONMENT_FILE_MISSING",
|
|
13584
|
+
message: `\`${project.name}:${target.name}:${configuration.name}\` sets \`esbuildConfig\` to \`${configuration.esbuildConfig}\`, which does not exist.`,
|
|
13585
|
+
group,
|
|
13586
|
+
project: project.name,
|
|
13587
|
+
file: project.configFile,
|
|
13588
|
+
line: void 0
|
|
13589
|
+
});
|
|
13590
|
+
}
|
|
13591
|
+
}
|
|
13592
|
+
}
|
|
13593
|
+
}
|
|
13594
|
+
}
|
|
13595
|
+
function checkUnreferencedEnvironmentFiles(config) {
|
|
13596
|
+
const { inspection, violations } = config;
|
|
13597
|
+
const group = "deploy";
|
|
13598
|
+
for (const project of inspection.projects) {
|
|
13599
|
+
const referenced = new Set(project.referencedEnvironmentFiles);
|
|
13600
|
+
for (const environmentFile of project.environmentFiles) {
|
|
13601
|
+
if (!referenced.has(environmentFile)) {
|
|
13602
|
+
pushViolation11(violations, {
|
|
13603
|
+
code: "WORKSPACE_DEPLOY_ENVIRONMENT_FILE_UNREFERENCED",
|
|
13604
|
+
message: `\`${environmentFile}\` is not selected by any build configuration of \`${project.name}\` \u2014 it is edited and reviewed but never compiled.`,
|
|
13605
|
+
severity: "warning",
|
|
13606
|
+
group,
|
|
13607
|
+
project: project.name,
|
|
13608
|
+
file: project.configFile,
|
|
13609
|
+
line: void 0
|
|
13610
|
+
});
|
|
13611
|
+
}
|
|
13612
|
+
}
|
|
13613
|
+
}
|
|
13614
|
+
}
|
|
13615
|
+
function checkHostingTargets(config) {
|
|
13616
|
+
const { inspection, violations } = config;
|
|
13617
|
+
const group = "deploy";
|
|
13618
|
+
if (!inspection.firebase.present) {
|
|
13619
|
+
return;
|
|
13620
|
+
}
|
|
13621
|
+
const declared = new Set(inspection.firebase.hostingTargets);
|
|
13622
|
+
for (const project of inspection.projects) {
|
|
13623
|
+
for (const target of project.targets) {
|
|
13624
|
+
for (const command of target.commands) {
|
|
13625
|
+
for (const hostingTarget of scanHostingTargets(command)) {
|
|
13626
|
+
if (!declared.has(hostingTarget)) {
|
|
13627
|
+
pushViolation11(violations, {
|
|
13628
|
+
code: "WORKSPACE_DEPLOY_HOSTING_TARGET_MISSING",
|
|
13629
|
+
message: `\`${project.name}:${target.name}\` deploys \`hosting:${hostingTarget}\`, which \`firebase.json\` does not declare (declared: [${Array.from(declared).join(", ") || "none"}]).`,
|
|
13630
|
+
group,
|
|
13631
|
+
project: project.name,
|
|
13632
|
+
file: project.configFile,
|
|
13633
|
+
line: void 0
|
|
13634
|
+
});
|
|
13635
|
+
}
|
|
13636
|
+
}
|
|
13637
|
+
}
|
|
13638
|
+
}
|
|
13639
|
+
}
|
|
13640
|
+
}
|
|
13641
|
+
function checkDeployLaneSymmetry(config) {
|
|
13642
|
+
const { inspection, violations } = config;
|
|
13643
|
+
const group = "deploy";
|
|
13644
|
+
const lanesByProject = /* @__PURE__ */ new Map();
|
|
13645
|
+
const allLanes = /* @__PURE__ */ new Set();
|
|
13646
|
+
for (const project of inspection.projects) {
|
|
13647
|
+
const lanes = /* @__PURE__ */ new Set();
|
|
13648
|
+
for (const target of project.targets) {
|
|
13649
|
+
const match = DEPLOY_LANE_PATTERN.exec(target.name);
|
|
13650
|
+
if (match !== null) {
|
|
13651
|
+
lanes.add(match[1]);
|
|
13652
|
+
allLanes.add(match[1]);
|
|
13653
|
+
}
|
|
13654
|
+
}
|
|
13655
|
+
if (lanes.size > 0) {
|
|
13656
|
+
lanesByProject.set(project.name, lanes);
|
|
13657
|
+
}
|
|
13658
|
+
}
|
|
13659
|
+
if (lanesByProject.size < 2) {
|
|
13660
|
+
return;
|
|
13661
|
+
}
|
|
13662
|
+
for (const project of inspection.projects) {
|
|
13663
|
+
const lanes = lanesByProject.get(project.name);
|
|
13664
|
+
if (lanes === void 0) {
|
|
13665
|
+
continue;
|
|
13666
|
+
}
|
|
13667
|
+
for (const lane of Array.from(allLanes).sort((a, b) => a.localeCompare(b))) {
|
|
13668
|
+
if (!lanes.has(lane)) {
|
|
13669
|
+
pushViolation11(violations, {
|
|
13670
|
+
code: "WORKSPACE_DEPLOY_LANE_ASYMMETRIC",
|
|
13671
|
+
message: `\`${project.name}\` deploys [${Array.from(lanes).sort((a, b) => a.localeCompare(b)).join(", ")}] but has no \`${DEPLOY_TARGET_PREFIX}${lane}\`, while a sibling project deploys \`${lane}\`.`,
|
|
13672
|
+
severity: "warning",
|
|
13673
|
+
group,
|
|
13674
|
+
project: project.name,
|
|
13675
|
+
file: project.configFile,
|
|
13676
|
+
line: void 0
|
|
13677
|
+
});
|
|
13678
|
+
}
|
|
13679
|
+
}
|
|
13680
|
+
}
|
|
13681
|
+
}
|
|
13682
|
+
function runRules9(input) {
|
|
13683
|
+
const { inspection, groups } = input;
|
|
13684
|
+
const violations = [];
|
|
13685
|
+
const targetsByProject = /* @__PURE__ */ new Map();
|
|
13686
|
+
for (const project of inspection.projects) {
|
|
13687
|
+
targetsByProject.set(project.name, indexTargets(project));
|
|
13688
|
+
}
|
|
13689
|
+
if (groups.includes("targets")) {
|
|
13690
|
+
checkTargetReferences({ inspection, targetsByProject, violations });
|
|
13691
|
+
checkTargetOutputs({ inspection, violations });
|
|
13692
|
+
}
|
|
13693
|
+
if (groups.includes("deploy")) {
|
|
13694
|
+
checkDeployLaneEnvironmentSelection({ inspection, targetsByProject, violations });
|
|
13695
|
+
checkConfigurationPaths({ inspection, violations });
|
|
13696
|
+
checkUnreferencedEnvironmentFiles({ inspection, violations });
|
|
13697
|
+
checkHostingTargets({ inspection, violations });
|
|
13698
|
+
checkDeployLaneSymmetry({ inspection, violations });
|
|
13699
|
+
}
|
|
13700
|
+
return violations;
|
|
13701
|
+
}
|
|
13702
|
+
|
|
13703
|
+
// packages/dbx-cli/validate/src/lib/workspace-validate/validate.ts
|
|
13704
|
+
function validateWorkspace(input) {
|
|
13705
|
+
const { inspection } = input;
|
|
13706
|
+
const groups = input.groups === void 0 || input.groups.length === 0 ? WORKSPACE_RULE_GROUPS : input.groups;
|
|
13707
|
+
const violations = runRules9({ inspection, groups });
|
|
13708
|
+
let errorCount = 0;
|
|
13709
|
+
let warningCount = 0;
|
|
13710
|
+
for (const violation of violations) {
|
|
13711
|
+
if (violation.severity === "error") {
|
|
13712
|
+
errorCount += 1;
|
|
13713
|
+
} else {
|
|
13714
|
+
warningCount += 1;
|
|
13715
|
+
}
|
|
13716
|
+
}
|
|
13717
|
+
return {
|
|
13718
|
+
violations,
|
|
13719
|
+
errorCount,
|
|
13720
|
+
warningCount,
|
|
13721
|
+
projectsChecked: inspection.projects.length,
|
|
13722
|
+
referencesChecked: inspection.references.length,
|
|
13723
|
+
groups
|
|
13724
|
+
};
|
|
13725
|
+
}
|
|
13726
|
+
|
|
12737
13727
|
// packages/dbx-cli/validate/src/lib/model-list-component/index.ts
|
|
12738
13728
|
var model_list_component_exports = {};
|
|
12739
13729
|
__export(model_list_component_exports, {
|
|
@@ -12744,8 +13734,8 @@ __export(model_list_component_exports, {
|
|
|
12744
13734
|
});
|
|
12745
13735
|
|
|
12746
13736
|
// packages/dbx-cli/model-test/src/lib/fixture/inspect.ts
|
|
12747
|
-
import { readFile as
|
|
12748
|
-
import { join as
|
|
13737
|
+
import { readFile as readFile12 } from "node:fs/promises";
|
|
13738
|
+
import { join as join11 } from "node:path";
|
|
12749
13739
|
|
|
12750
13740
|
// packages/dbx-cli/model-test/src/lib/fixture/extract.ts
|
|
12751
13741
|
import { Node as Node16, Project as Project9, SyntaxKind as SyntaxKind7 } from "ts-morph";
|
|
@@ -13242,21 +14232,21 @@ function collectIdentityImports(sourceFile) {
|
|
|
13242
14232
|
// packages/dbx-cli/model-test/src/lib/fixture/inspect.ts
|
|
13243
14233
|
var FIXTURE_RELATIVE_PATH = "src/test/fixture.ts";
|
|
13244
14234
|
async function inspectAppFixtures(apiAbs, apiRel) {
|
|
13245
|
-
const absolutePath =
|
|
14235
|
+
const absolutePath = join11(apiAbs, FIXTURE_RELATIVE_PATH);
|
|
13246
14236
|
const relativePath = `${apiRel}/${FIXTURE_RELATIVE_PATH}`;
|
|
13247
|
-
const text = await
|
|
14237
|
+
const text = await readFile12(absolutePath, "utf8");
|
|
13248
14238
|
return extractAppFixturesFromText({ text, fixturePath: relativePath });
|
|
13249
14239
|
}
|
|
13250
14240
|
|
|
13251
14241
|
// packages/dbx-cli/validate/src/lib/model-list-component/extract.ts
|
|
13252
|
-
import { readdir as
|
|
13253
|
-
import { basename as
|
|
14242
|
+
import { readdir as readdir9, readFile as readFile13, stat as stat11 } from "node:fs/promises";
|
|
14243
|
+
import { basename as basename6, join as join12, relative as relative7, sep as sep7 } from "node:path";
|
|
13254
14244
|
import { Node as Node17, Project as Project10 } from "ts-morph";
|
|
13255
14245
|
var MODEL_SUBPATH = "src/lib/model";
|
|
13256
14246
|
var MODEL_SUBPATH_PREFIX = `${MODEL_SUBPATH}/`;
|
|
13257
14247
|
var RESERVED_NAMES = RESERVED_MODEL_FOLDERS.map((r) => r.name);
|
|
13258
14248
|
async function extractComponentModels(componentAbs) {
|
|
13259
|
-
const modelRoot =
|
|
14249
|
+
const modelRoot = join12(componentAbs, MODEL_SUBPATH);
|
|
13260
14250
|
const folders = await listFolders(modelRoot);
|
|
13261
14251
|
if (folders === void 0) {
|
|
13262
14252
|
const result2 = { modelRoot, models: [], skipped: [], unidentifiedFolders: [] };
|
|
@@ -13346,13 +14336,13 @@ function projectRich(model, folder) {
|
|
|
13346
14336
|
}
|
|
13347
14337
|
async function fallbackForFolder(input) {
|
|
13348
14338
|
const { modelRoot, componentAbs, folder, project } = input;
|
|
13349
|
-
const folderAbs =
|
|
14339
|
+
const folderAbs = join12(modelRoot, folder);
|
|
13350
14340
|
const mainFile = await readMainFile(folderAbs, folder);
|
|
13351
14341
|
if (mainFile === void 0) return void 0;
|
|
13352
14342
|
const sourceFile = project.createSourceFile(mainFile.fileName, mainFile.text, { overwrite: true });
|
|
13353
14343
|
const identity = findIdentityIn(sourceFile);
|
|
13354
14344
|
if (!identity) return void 0;
|
|
13355
|
-
const sourceFileRel =
|
|
14345
|
+
const sourceFileRel = relative7(componentAbs, mainFile.absPath).split(sep7).join("/");
|
|
13356
14346
|
return {
|
|
13357
14347
|
folder,
|
|
13358
14348
|
modelName: deriveModelName(identity.constName, folder),
|
|
@@ -13368,13 +14358,13 @@ async function listFolders(modelRoot) {
|
|
|
13368
14358
|
let result;
|
|
13369
14359
|
let isDir = false;
|
|
13370
14360
|
try {
|
|
13371
|
-
const stats = await
|
|
14361
|
+
const stats = await stat11(modelRoot);
|
|
13372
14362
|
isDir = stats.isDirectory();
|
|
13373
14363
|
} catch {
|
|
13374
14364
|
isDir = false;
|
|
13375
14365
|
}
|
|
13376
14366
|
if (isDir) {
|
|
13377
|
-
const entries = await
|
|
14367
|
+
const entries = await readdir9(modelRoot, { withFileTypes: true });
|
|
13378
14368
|
const out = [];
|
|
13379
14369
|
for (const entry of entries) {
|
|
13380
14370
|
if (entry.isDirectory()) out.push(entry.name);
|
|
@@ -13385,11 +14375,11 @@ async function listFolders(modelRoot) {
|
|
|
13385
14375
|
return result;
|
|
13386
14376
|
}
|
|
13387
14377
|
async function readMainFile(folderAbs, folder) {
|
|
13388
|
-
const candidate =
|
|
14378
|
+
const candidate = join12(folderAbs, `${folder}.ts`);
|
|
13389
14379
|
let result;
|
|
13390
14380
|
try {
|
|
13391
|
-
const text = await
|
|
13392
|
-
result = { absPath: candidate, fileName:
|
|
14381
|
+
const text = await readFile13(candidate, "utf8");
|
|
14382
|
+
result = { absPath: candidate, fileName: basename6(candidate), text };
|
|
13393
14383
|
} catch {
|
|
13394
14384
|
result = void 0;
|
|
13395
14385
|
}
|
|
@@ -13534,21 +14524,21 @@ __export(model_api_list_app_exports, {
|
|
|
13534
14524
|
});
|
|
13535
14525
|
|
|
13536
14526
|
// packages/dbx-cli/validate/src/lib/model-api-list-app/extract.ts
|
|
13537
|
-
import { readFile as
|
|
13538
|
-
import { join as
|
|
14527
|
+
import { readFile as readFile14 } from "node:fs/promises";
|
|
14528
|
+
import { join as join13, relative as relative8, sep as sep8 } from "node:path";
|
|
13539
14529
|
var API_SUFFIX2 = ".api.ts";
|
|
13540
14530
|
var MODEL_SUBPATH2 = "src/lib";
|
|
13541
14531
|
async function extractApiList(input) {
|
|
13542
|
-
const modelRoot =
|
|
14532
|
+
const modelRoot = join13(input.componentAbs, MODEL_SUBPATH2);
|
|
13543
14533
|
const apiFiles = await collectFilesWithSuffix(modelRoot, API_SUFFIX2);
|
|
13544
14534
|
const entries = [];
|
|
13545
14535
|
const files = [];
|
|
13546
14536
|
for (const filePath of apiFiles) {
|
|
13547
|
-
const text = await
|
|
14537
|
+
const text = await readFile14(filePath, "utf8");
|
|
13548
14538
|
if (!text.includes("callModelFirebaseFunctionMapFactory")) {
|
|
13549
14539
|
continue;
|
|
13550
14540
|
}
|
|
13551
|
-
const sourceFileRel =
|
|
14541
|
+
const sourceFileRel = relative8(input.componentAbs, filePath).split(sep8).join("/");
|
|
13552
14542
|
const extraction = extractCrudEntries({ name: sourceFileRel, text });
|
|
13553
14543
|
const fileEntries = extraction.entries.map((entry) => ({ ...entry, sourceFile: sourceFileRel }));
|
|
13554
14544
|
const counts = countVerbs(fileEntries);
|
|
@@ -13679,17 +14669,17 @@ __export(model_api_lookup_exports, {
|
|
|
13679
14669
|
});
|
|
13680
14670
|
|
|
13681
14671
|
// packages/dbx-cli/validate/src/lib/model-api-lookup/extract.ts
|
|
13682
|
-
import { readFile as
|
|
13683
|
-
import { join as
|
|
14672
|
+
import { readFile as readFile16 } from "node:fs/promises";
|
|
14673
|
+
import { join as join15, relative as relative10, sep as sep10 } from "node:path";
|
|
13684
14674
|
|
|
13685
14675
|
// packages/dbx-cli/validate/src/lib/model-api-lookup/extract-actions.ts
|
|
13686
|
-
import { readFile as
|
|
13687
|
-
import { join as
|
|
14676
|
+
import { readFile as readFile15 } from "node:fs/promises";
|
|
14677
|
+
import { join as join14, relative as relative9, sep as sep9 } from "node:path";
|
|
13688
14678
|
import { Project as Project11, SyntaxKind as SyntaxKind8 } from "ts-morph";
|
|
13689
14679
|
var ACTION_SERVER_SUFFIX = ".action.server.ts";
|
|
13690
14680
|
var COMMON_MODEL_SUBPATH = "src/app/common/model";
|
|
13691
14681
|
async function buildActionLookup(apiAbs) {
|
|
13692
|
-
const modelRoot =
|
|
14682
|
+
const modelRoot = join14(apiAbs, COMMON_MODEL_SUBPATH);
|
|
13693
14683
|
const files = await collectFilesWithSuffix(modelRoot, ACTION_SERVER_SUFFIX);
|
|
13694
14684
|
const methodsByParams = /* @__PURE__ */ new Map();
|
|
13695
14685
|
const factoriesByParams = /* @__PURE__ */ new Map();
|
|
@@ -13698,9 +14688,9 @@ async function buildActionLookup(apiAbs) {
|
|
|
13698
14688
|
}
|
|
13699
14689
|
const project = new Project11({ useInMemoryFileSystem: true, skipAddingFilesFromTsConfig: true });
|
|
13700
14690
|
for (const filePath of files) {
|
|
13701
|
-
const text = await
|
|
14691
|
+
const text = await readFile15(filePath, "utf8");
|
|
13702
14692
|
const sourceFile = project.createSourceFile(filePath, text, { overwrite: true });
|
|
13703
|
-
const sourceFileRel =
|
|
14693
|
+
const sourceFileRel = relative9(apiAbs, filePath).split(sep9).join("/");
|
|
13704
14694
|
collectAbstractMethods({ sourceFile, sourceFileRel, methodsByParams });
|
|
13705
14695
|
collectFactoryFunctions({ sourceFile, sourceFileRel, factoriesByParams });
|
|
13706
14696
|
}
|
|
@@ -13860,13 +14850,13 @@ function mapDocFields(fields) {
|
|
|
13860
14850
|
return result;
|
|
13861
14851
|
}
|
|
13862
14852
|
async function collectApiSources(componentAbs) {
|
|
13863
|
-
const root =
|
|
14853
|
+
const root = join15(componentAbs, COMPONENT_LIB_SUBPATH2);
|
|
13864
14854
|
const files = await collectFilesWithSuffix(root, API_SUFFIX3);
|
|
13865
14855
|
const sources = [];
|
|
13866
14856
|
for (const fileAbs of files) {
|
|
13867
|
-
const text = await
|
|
14857
|
+
const text = await readFile16(fileAbs, "utf8");
|
|
13868
14858
|
if (!text.includes("callModelFirebaseFunctionMapFactory")) continue;
|
|
13869
|
-
const fileRel =
|
|
14859
|
+
const fileRel = relative10(componentAbs, fileAbs).split(sep10).join("/");
|
|
13870
14860
|
sources.push({ fileAbs, fileRel, text });
|
|
13871
14861
|
}
|
|
13872
14862
|
return sources;
|
|
@@ -14014,14 +15004,14 @@ __export(server_actions_list_app_exports, {
|
|
|
14014
15004
|
});
|
|
14015
15005
|
|
|
14016
15006
|
// packages/dbx-cli/validate/src/lib/server-actions-list-app/extract.ts
|
|
14017
|
-
import { readdir as
|
|
14018
|
-
import { dirname as
|
|
15007
|
+
import { readdir as readdir10, readFile as readFile17 } from "node:fs/promises";
|
|
15008
|
+
import { dirname as dirname3, join as join16, relative as relative11, sep as sep11 } from "node:path";
|
|
14019
15009
|
import { Node as Node18, Project as Project12 } from "ts-morph";
|
|
14020
15010
|
var ACTION_SERVER_SUFFIX2 = ".action.server.ts";
|
|
14021
15011
|
var COMMON_MODEL_SUBPATH2 = "src/app/common/model";
|
|
14022
15012
|
var COMMON_BARREL_REL = "src/app/common/index.ts";
|
|
14023
15013
|
async function extractServerActions(apiAbs, apiRel) {
|
|
14024
|
-
const modelRoot =
|
|
15014
|
+
const modelRoot = join16(apiAbs, COMMON_MODEL_SUBPATH2);
|
|
14025
15015
|
const actionFiles = await collectFilesWithSuffix(modelRoot, ACTION_SERVER_SUFFIX2);
|
|
14026
15016
|
if (actionFiles.length === 0) {
|
|
14027
15017
|
return { modelRoot, entries: [], fixtureStatus: "ok" };
|
|
@@ -14041,14 +15031,14 @@ async function extractServerActions(apiAbs, apiRel) {
|
|
|
14041
15031
|
}
|
|
14042
15032
|
const entries = [];
|
|
14043
15033
|
for (const filePath of actionFiles) {
|
|
14044
|
-
const text = await
|
|
15034
|
+
const text = await readFile17(filePath, "utf8");
|
|
14045
15035
|
const sourceFile = project.createSourceFile(filePath, text, { overwrite: true });
|
|
14046
15036
|
const classes = findServerActionClasses(sourceFile);
|
|
14047
15037
|
for (const className of classes) {
|
|
14048
15038
|
const wiring = await inspectSiblingModule(filePath, className);
|
|
14049
15039
|
const expectedGetterName = camelize(className);
|
|
14050
15040
|
const fixtureCoverage = fixtureStatus === "ok" ? fixtureCoverageMap.get(expectedGetterName) ?? unmatchedFixtureCoverage(expectedGetterName) : void 0;
|
|
14051
|
-
const sourceFileRel =
|
|
15041
|
+
const sourceFileRel = relative11(apiAbs, filePath).split(sep11).join("/");
|
|
14052
15042
|
entries.push({
|
|
14053
15043
|
className,
|
|
14054
15044
|
sourceFile: sourceFileRel,
|
|
@@ -14090,14 +15080,14 @@ async function inspectSiblingModule(actionFilePath, className) {
|
|
|
14090
15080
|
return { modulePath: moduleCandidates[0], providedByModule: false, exportedByModule: false };
|
|
14091
15081
|
}
|
|
14092
15082
|
async function findModuleCandidates(actionFilePath) {
|
|
14093
|
-
const dir =
|
|
15083
|
+
const dir = dirname3(actionFilePath);
|
|
14094
15084
|
const out = [];
|
|
14095
15085
|
try {
|
|
14096
|
-
const entries = await
|
|
15086
|
+
const entries = await readdir10(dir, { withFileTypes: true });
|
|
14097
15087
|
for (const entry of entries) {
|
|
14098
15088
|
if (!entry.isFile()) continue;
|
|
14099
15089
|
if (!entry.name.endsWith(".module.ts")) continue;
|
|
14100
|
-
out.push(
|
|
15090
|
+
out.push(join16(dir, entry.name));
|
|
14101
15091
|
}
|
|
14102
15092
|
} catch {
|
|
14103
15093
|
}
|
|
@@ -14107,7 +15097,7 @@ async function findModuleCandidates(actionFilePath) {
|
|
|
14107
15097
|
async function safeRead(path) {
|
|
14108
15098
|
let result;
|
|
14109
15099
|
try {
|
|
14110
|
-
result = await
|
|
15100
|
+
result = await readFile17(path, "utf8");
|
|
14111
15101
|
} catch {
|
|
14112
15102
|
result = void 0;
|
|
14113
15103
|
}
|
|
@@ -14154,7 +15144,7 @@ function moduleArrayContains(decoratorArg, propertyName, className) {
|
|
|
14154
15144
|
return false;
|
|
14155
15145
|
}
|
|
14156
15146
|
async function readCommonBarrelExports(apiAbs) {
|
|
14157
|
-
const path =
|
|
15147
|
+
const path = join16(apiAbs, COMMON_BARREL_REL);
|
|
14158
15148
|
const text = await safeRead(path);
|
|
14159
15149
|
if (text === void 0) return /* @__PURE__ */ new Set();
|
|
14160
15150
|
const project = new Project12({ useInMemoryFileSystem: true });
|
|
@@ -14333,8 +15323,8 @@ function formatReportAsJson4(report) {
|
|
|
14333
15323
|
|
|
14334
15324
|
// packages/dbx-cli/validate/src/lib/dbx-asset-list-app/format.markdown.ts
|
|
14335
15325
|
function formatReportAsMarkdown4(report) {
|
|
14336
|
-
const
|
|
14337
|
-
const lines = [`# App assets \u2014 ${
|
|
15326
|
+
const basename7 = report.componentDir.split("/").pop() ?? report.componentDir;
|
|
15327
|
+
const lines = [`# App assets \u2014 ${basename7}`, "", `Component: \`${report.componentDir}\``, `App: \`${report.apiDir}\``, "", `\`src/lib/assets.ts\`: ${formatBool(report.assetsFileExists)}`, `Barrel re-exports \`./assets\`: ${formatBool(report.barrelReExportsAssets)}`, `\`provideDbxAssetLoader()\` called in \`src/root.app.config.ts\`: ${formatBool(report.providerWiredInApp)}`, "", `## Assets (${report.assets.length})`];
|
|
14338
15328
|
if (report.assets.length === 0) {
|
|
14339
15329
|
lines.push("", "_None found._");
|
|
14340
15330
|
} else {
|
|
@@ -14522,11 +15512,11 @@ function formatReportAsJson5(report) {
|
|
|
14522
15512
|
|
|
14523
15513
|
// packages/dbx-cli/validate/src/lib/notification-m-list-app/format.markdown.ts
|
|
14524
15514
|
function formatReportAsMarkdown5(report) {
|
|
14525
|
-
const
|
|
15515
|
+
const basename7 = report.componentDir.split("/").pop() ?? report.componentDir;
|
|
14526
15516
|
const aggregatorRecordText = report.aggregatorRecordName ? code(report.aggregatorRecordName) : "_Not defined._";
|
|
14527
15517
|
const templateConfigsFactoryText = report.templateConfigsArrayFactoryName ? code(report.templateConfigsArrayFactoryName) : "_Not defined._";
|
|
14528
15518
|
const lines = [
|
|
14529
|
-
`# App notifications \u2014 ${
|
|
15519
|
+
`# App notifications \u2014 ${basename7}`,
|
|
14530
15520
|
"",
|
|
14531
15521
|
`Component: \`${report.componentDir}\``,
|
|
14532
15522
|
`API: \`${report.apiDir}\``,
|
|
@@ -14723,9 +15713,9 @@ function formatReportAsJson6(report) {
|
|
|
14723
15713
|
|
|
14724
15714
|
// packages/dbx-cli/validate/src/lib/storagefile-m-list-app/format.markdown.ts
|
|
14725
15715
|
function formatReportAsMarkdown6(report) {
|
|
14726
|
-
const
|
|
15716
|
+
const basename7 = report.componentDir.split("/").pop() ?? report.componentDir;
|
|
14727
15717
|
const uploadServiceFactoryText = report.uploadServiceFactoryName ? code2(report.uploadServiceFactoryName) : "_Not defined._";
|
|
14728
|
-
const lines = [`# App storagefiles \u2014 ${
|
|
15718
|
+
const lines = [`# App storagefiles \u2014 ${basename7}`, "", `Component: \`${report.componentDir}\``, `API: \`${report.apiDir}\``, "", `Upload service factory: ${uploadServiceFactoryText}`, `Wired via \`StorageFileInitializeFromUploadService\` provider: ${formatBool3(report.uploadServiceWiredInApi)}`, `Processing handler call present: ${formatBool3(report.processingHandlerWiredInApi)}`, "", `## Purposes (${report.purposes.length})`];
|
|
14729
15719
|
if (report.purposes.length === 0) {
|
|
14730
15720
|
lines.push("", "_None found._");
|
|
14731
15721
|
} else {
|
|
@@ -14909,8 +15899,8 @@ __export(dbx_color_template_list_app_exports, {
|
|
|
14909
15899
|
});
|
|
14910
15900
|
|
|
14911
15901
|
// packages/dbx-cli/validate/src/lib/dbx-color-template-list-app/inspect.ts
|
|
14912
|
-
import { readFile as
|
|
14913
|
-
import { join as
|
|
15902
|
+
import { readFile as readFile18 } from "node:fs/promises";
|
|
15903
|
+
import { join as join17, relative as relative12, sep as sep12 } from "node:path";
|
|
14914
15904
|
var CANDIDATE_RELPATHS = ["src/root.app.config.ts", "src/app.config.ts", "src/app.module.ts", "src/app/app.config.ts", "src/app/app.module.ts"];
|
|
14915
15905
|
async function inspectColorTemplates(apiAbs, apiRel) {
|
|
14916
15906
|
const present = [];
|
|
@@ -14920,7 +15910,7 @@ async function inspectColorTemplates(apiAbs, apiRel) {
|
|
|
14920
15910
|
for (const candidate of candidates) {
|
|
14921
15911
|
const text = await readFileIfExists2(candidate);
|
|
14922
15912
|
if (text === void 0) continue;
|
|
14923
|
-
const rel =
|
|
15913
|
+
const rel = relative12(apiAbs, candidate).split(sep12).join("/");
|
|
14924
15914
|
present.push({ relPath: rel, text });
|
|
14925
15915
|
}
|
|
14926
15916
|
} catch (err) {
|
|
@@ -14937,25 +15927,25 @@ async function inspectColorTemplates(apiAbs, apiRel) {
|
|
|
14937
15927
|
async function collectCandidatePaths(apiAbs) {
|
|
14938
15928
|
const explicit = [];
|
|
14939
15929
|
for (const rel of CANDIDATE_RELPATHS) {
|
|
14940
|
-
explicit.push(
|
|
15930
|
+
explicit.push(join17(apiAbs, rel));
|
|
14941
15931
|
}
|
|
14942
|
-
const scanned = await scanProviderFiles(
|
|
15932
|
+
const scanned = await scanProviderFiles(join17(apiAbs, "src"));
|
|
14943
15933
|
return [...explicit, ...scanned];
|
|
14944
15934
|
}
|
|
14945
15935
|
async function scanProviderFiles(srcAbs) {
|
|
14946
|
-
const { readdir:
|
|
15936
|
+
const { readdir: readdir11 } = await import("node:fs/promises");
|
|
14947
15937
|
const out = [];
|
|
14948
15938
|
const stack = [srcAbs];
|
|
14949
15939
|
while (stack.length > 0) {
|
|
14950
15940
|
const current = stack.pop();
|
|
14951
15941
|
let entries;
|
|
14952
15942
|
try {
|
|
14953
|
-
entries = await
|
|
15943
|
+
entries = await readdir11(current, { withFileTypes: true });
|
|
14954
15944
|
} catch {
|
|
14955
15945
|
continue;
|
|
14956
15946
|
}
|
|
14957
15947
|
for (const entry of entries) {
|
|
14958
|
-
const full =
|
|
15948
|
+
const full = join17(current, entry.name);
|
|
14959
15949
|
if (entry.isDirectory()) {
|
|
14960
15950
|
stack.push(full);
|
|
14961
15951
|
continue;
|
|
@@ -14970,7 +15960,7 @@ async function scanProviderFiles(srcAbs) {
|
|
|
14970
15960
|
async function readFileIfExists2(absPath) {
|
|
14971
15961
|
let result;
|
|
14972
15962
|
try {
|
|
14973
|
-
result = await
|
|
15963
|
+
result = await readFile18(absPath, "utf8");
|
|
14974
15964
|
} catch (err) {
|
|
14975
15965
|
const code3 = err.code;
|
|
14976
15966
|
if (code3 !== "ENOENT" && code3 !== "ENOTDIR" && code3 !== "EISDIR") {
|
|
@@ -15674,11 +16664,12 @@ function matchKindForRadius(exact, raw) {
|
|
|
15674
16664
|
if (exact.defaults.light === raw || exact.defaults.dark === raw) return "(exact-match design-system corner token).";
|
|
15675
16665
|
return "(idiomatic full / pill radius \u2014 design system token).";
|
|
15676
16666
|
}
|
|
15677
|
-
function findExactRoleToken(
|
|
16667
|
+
function findExactRoleToken(params) {
|
|
16668
|
+
const { registry, raw, role, isAllowed } = params;
|
|
15678
16669
|
const candidates = registry.byRole.get(role) ?? [];
|
|
15679
16670
|
let result;
|
|
15680
16671
|
for (const entry of candidates) {
|
|
15681
|
-
if (entry.defaults.light === raw || entry.defaults.dark === raw) {
|
|
16672
|
+
if ((entry.defaults.light === raw || entry.defaults.dark === raw) && (isAllowed === void 0 || isAllowed(entry))) {
|
|
15682
16673
|
result = entry;
|
|
15683
16674
|
break;
|
|
15684
16675
|
}
|
|
@@ -15823,7 +16814,7 @@ var hardcodedRadius = (input) => {
|
|
|
15823
16814
|
const colonIndex = input.scss.indexOf(":", match.index);
|
|
15824
16815
|
if (colonIndex !== -1 && isInsideScssVarDeclaration(input.scss, colonIndex)) continue;
|
|
15825
16816
|
const raw = match[1];
|
|
15826
|
-
let exact = findExactRoleToken(input.tokenRegistry, raw, "radius");
|
|
16817
|
+
let exact = findExactRoleToken({ registry: input.tokenRegistry, raw, role: "radius" });
|
|
15827
16818
|
if (exact === void 0 && FULL_RADIUS_SYNONYMS.has(raw)) {
|
|
15828
16819
|
exact = input.tokenRegistry.findByCssVariable("--mat-sys-corner-full");
|
|
15829
16820
|
}
|
|
@@ -15854,7 +16845,7 @@ var hardcodedShadow = (input) => {
|
|
|
15854
16845
|
if (match.index === void 0) continue;
|
|
15855
16846
|
const raw = match[1].trim();
|
|
15856
16847
|
if (raw.startsWith("var(") || raw === "none") continue;
|
|
15857
|
-
const exact = findExactRoleToken(input.tokenRegistry, raw, "elevation");
|
|
16848
|
+
const exact = findExactRoleToken({ registry: input.tokenRegistry, raw, role: "elevation" });
|
|
15858
16849
|
const fix = exact === void 0 ? 'Hardcoded `box-shadow:` declarations should map to one of the `--mat-sys-level0..5` elevation tokens, or wrap the surface in `<dbx-content-box [elevate]="true">` so the shadow comes from the design system.' : `Replace this hardcoded shadow with \`box-shadow: var(${exact.cssVariable})\` or use \`<dbx-content-box [elevate]="true">\` to apply the elevation through the wrapper component.`;
|
|
15859
16850
|
matches.push({
|
|
15860
16851
|
id: "hardcoded-shadow",
|
|
@@ -15899,12 +16890,13 @@ var hardcodedHintColor = (input) => {
|
|
|
15899
16890
|
return matches;
|
|
15900
16891
|
};
|
|
15901
16892
|
var HARDCODED_PADDING_RE = /(?:padding|margin)\s*(?:-(?:top|right|bottom|left))?\s*:\s*(\d+(?:\.\d+)?px)(?!\s*\))/gi;
|
|
16893
|
+
var DBX_PADDING_SCALE_RE = /^--dbx-padding-\d+$/;
|
|
15902
16894
|
var hardcodedPadding = (input) => {
|
|
15903
16895
|
const matches = [];
|
|
15904
16896
|
for (const match of input.scss.matchAll(HARDCODED_PADDING_RE)) {
|
|
15905
16897
|
if (match.index === void 0) continue;
|
|
15906
16898
|
const raw = match[1];
|
|
15907
|
-
const exact = findExactRoleToken(input.tokenRegistry, raw, "spacing");
|
|
16899
|
+
const exact = findExactRoleToken({ registry: input.tokenRegistry, raw, role: "spacing", isAllowed: (entry) => DBX_PADDING_SCALE_RE.test(entry.cssVariable) });
|
|
15908
16900
|
if (exact !== void 0) {
|
|
15909
16901
|
const utility = exact.utilityClasses?.[0];
|
|
15910
16902
|
const fix = ["Replace `" + match[0].trim() + "` with `var(" + exact.cssVariable + ")`" + (utility === void 0 ? "" : ` (or use the \`${utility}\` utility class on the host element)`) + "."].join("\n");
|
|
@@ -16642,5 +17634,6 @@ export {
|
|
|
16642
17634
|
storagefile_m_validate_folder_exports as storagefileValidateFolder,
|
|
16643
17635
|
system_m_list_app_exports as systemListApp,
|
|
16644
17636
|
system_m_validate_folder_exports as systemValidateFolder,
|
|
16645
|
-
ui_smell_check_exports as uiSmellCheck
|
|
17637
|
+
ui_smell_check_exports as uiSmellCheck,
|
|
17638
|
+
workspace_validate_exports as workspaceValidate
|
|
16646
17639
|
};
|