@dereekb/dbx-cli 13.43.0 → 14.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/eslint/index.esm.js +2 -2
- package/eslint/package.json +7 -7
- package/firebase-api-manifest/main.js +65 -27
- package/firebase-api-manifest/package.json +5 -5
- package/firestore-query-manifest/main.js +46 -14
- package/firestore-query-manifest/package.json +4 -4
- package/generate-firestore-indexes/main.js +14 -6
- 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 +1663 -1996
- package/lint-cache/main.js +239 -69
- 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/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/scan-helpers/emit-generated-ts.d.ts +16 -4
- package/test/index.esm.js +19 -22
- package/test/package.json +11 -11
- package/validate/index.js +1149 -77
- 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
|
|
|
@@ -2970,7 +3060,31 @@ function attachRemediation(code3) {
|
|
|
2970
3060
|
|
|
2971
3061
|
// packages/dbx-cli/validate/src/lib/_core/rule-catalog/format.ts
|
|
2972
3062
|
function formatRuleAsMarkdown(entry) {
|
|
2973
|
-
const lines = [
|
|
3063
|
+
const lines = [
|
|
3064
|
+
`# \`${entry.code}\``,
|
|
3065
|
+
"",
|
|
3066
|
+
`**${entry.title}**`,
|
|
3067
|
+
"",
|
|
3068
|
+
`- **Severity:** \`${entry.severity}\``,
|
|
3069
|
+
`- **Source tool:** \`${entry.source}\``,
|
|
3070
|
+
"",
|
|
3071
|
+
"## What it flags",
|
|
3072
|
+
"",
|
|
3073
|
+
entry.whatItFlags,
|
|
3074
|
+
"",
|
|
3075
|
+
"## When it applies",
|
|
3076
|
+
"",
|
|
3077
|
+
entry.whenItApplies,
|
|
3078
|
+
"",
|
|
3079
|
+
"## When it does NOT apply",
|
|
3080
|
+
"",
|
|
3081
|
+
entry.whenItDoesNotApply,
|
|
3082
|
+
"",
|
|
3083
|
+
"## Canonical fix",
|
|
3084
|
+
"",
|
|
3085
|
+
entry.canonicalFix,
|
|
3086
|
+
""
|
|
3087
|
+
];
|
|
2974
3088
|
if (entry.canonicalFixTemplate) {
|
|
2975
3089
|
lines.push("## Fix template", "", entry.canonicalFixTemplate, "");
|
|
2976
3090
|
}
|
|
@@ -2996,22 +3110,22 @@ function formatSeeAlso(ref) {
|
|
|
2996
3110
|
// packages/dbx-cli/validate/src/lib/_core/validate-input.ts
|
|
2997
3111
|
import { glob as fsGlob, readFile, stat } from "node:fs/promises";
|
|
2998
3112
|
import { resolve, sep } from "node:path";
|
|
2999
|
-
function ensurePathInsideCwd(
|
|
3113
|
+
function ensurePathInsideCwd(relative13, cwd) {
|
|
3000
3114
|
const cwdPrefix = cwd.endsWith(sep) ? cwd : cwd + sep;
|
|
3001
|
-
const absolute = resolve(cwd,
|
|
3115
|
+
const absolute = resolve(cwd, relative13);
|
|
3002
3116
|
if (!absolute.startsWith(cwdPrefix) && absolute !== cwd) {
|
|
3003
|
-
throw new Error(`Path \`${
|
|
3117
|
+
throw new Error(`Path \`${relative13}\` resolves outside the server cwd and is not allowed.`);
|
|
3004
3118
|
}
|
|
3005
3119
|
}
|
|
3006
3120
|
async function resolveFolderPaths(config) {
|
|
3007
3121
|
const { paths, glob, cwd } = config;
|
|
3008
3122
|
const collected = [];
|
|
3009
3123
|
const seen = /* @__PURE__ */ new Set();
|
|
3010
|
-
const accept = (
|
|
3011
|
-
if (seen.has(
|
|
3012
|
-
ensurePathInsideCwd(
|
|
3013
|
-
seen.add(
|
|
3014
|
-
collected.push(
|
|
3124
|
+
const accept = (relative13) => {
|
|
3125
|
+
if (seen.has(relative13)) return;
|
|
3126
|
+
ensurePathInsideCwd(relative13, cwd);
|
|
3127
|
+
seen.add(relative13);
|
|
3128
|
+
collected.push(relative13);
|
|
3015
3129
|
};
|
|
3016
3130
|
if (paths) {
|
|
3017
3131
|
for (const p of paths) {
|
|
@@ -3054,13 +3168,13 @@ async function resolveValidatorSources(config) {
|
|
|
3054
3168
|
pathList.push(match);
|
|
3055
3169
|
}
|
|
3056
3170
|
}
|
|
3057
|
-
for (const
|
|
3058
|
-
if (seenNames.has(
|
|
3059
|
-
ensurePathInsideCwd(
|
|
3060
|
-
const absolute = resolve(cwd,
|
|
3171
|
+
for (const relative13 of pathList) {
|
|
3172
|
+
if (seenNames.has(relative13)) continue;
|
|
3173
|
+
ensurePathInsideCwd(relative13, cwd);
|
|
3174
|
+
const absolute = resolve(cwd, relative13);
|
|
3061
3175
|
const text = await readFile(absolute, "utf8");
|
|
3062
|
-
seenNames.add(
|
|
3063
|
-
collected.push({ name:
|
|
3176
|
+
seenNames.add(relative13);
|
|
3177
|
+
collected.push({ name: relative13, text });
|
|
3064
3178
|
}
|
|
3065
3179
|
return collected;
|
|
3066
3180
|
}
|
|
@@ -3115,12 +3229,12 @@ function formatRemediationBlock(hint) {
|
|
|
3115
3229
|
return lines.join("\n");
|
|
3116
3230
|
}
|
|
3117
3231
|
function aggregateFolderRules(config) {
|
|
3118
|
-
const { inspections, runRules:
|
|
3232
|
+
const { inspections, runRules: runRules10 } = config;
|
|
3119
3233
|
const violations = [];
|
|
3120
3234
|
let errorCount = 0;
|
|
3121
3235
|
let warningCount = 0;
|
|
3122
3236
|
for (const inspection of inspections) {
|
|
3123
|
-
const folderViolations =
|
|
3237
|
+
const folderViolations = runRules10(inspection);
|
|
3124
3238
|
for (const v of folderViolations) {
|
|
3125
3239
|
violations.push(v);
|
|
3126
3240
|
if (v.severity === "error") {
|
|
@@ -12739,6 +12853,939 @@ function appendDiagnosticEntry(lines, d) {
|
|
|
12739
12853
|
}
|
|
12740
12854
|
}
|
|
12741
12855
|
|
|
12856
|
+
// packages/dbx-cli/validate/src/lib/workspace-validate/index.ts
|
|
12857
|
+
var workspace_validate_exports = {};
|
|
12858
|
+
__export(workspace_validate_exports, {
|
|
12859
|
+
DEPLOY_TARGET_PREFIX: () => DEPLOY_TARGET_PREFIX,
|
|
12860
|
+
WORKSPACE_RULE_GROUPS: () => WORKSPACE_RULE_GROUPS,
|
|
12861
|
+
WorkspaceValidateCode: () => WorkspaceValidateCode,
|
|
12862
|
+
formatResult: () => formatResult11,
|
|
12863
|
+
inspectWorkspace: () => inspectWorkspace,
|
|
12864
|
+
pathEscapesWorkspaceRoot: () => pathEscapesWorkspaceRoot,
|
|
12865
|
+
scanDocumentTargetReferences: () => scanDocumentTargetReferences,
|
|
12866
|
+
scanHostingTargets: () => scanHostingTargets,
|
|
12867
|
+
scanTargetReferences: () => scanTargetReferences,
|
|
12868
|
+
validateWorkspace: () => validateWorkspace
|
|
12869
|
+
});
|
|
12870
|
+
|
|
12871
|
+
// packages/dbx-cli/validate/src/lib/workspace-validate/codes.ts
|
|
12872
|
+
var WorkspaceValidateCode = /* @__PURE__ */ ((WorkspaceValidateCode2) => {
|
|
12873
|
+
WorkspaceValidateCode2["WORKSPACE_TARGET_REF_PROJECT_MISSING"] = "WORKSPACE_TARGET_REF_PROJECT_MISSING";
|
|
12874
|
+
WorkspaceValidateCode2["WORKSPACE_TARGET_REF_TARGET_MISSING"] = "WORKSPACE_TARGET_REF_TARGET_MISSING";
|
|
12875
|
+
WorkspaceValidateCode2["WORKSPACE_TARGET_REF_CONFIGURATION_MISSING"] = "WORKSPACE_TARGET_REF_CONFIGURATION_MISSING";
|
|
12876
|
+
WorkspaceValidateCode2["WORKSPACE_TARGET_OUTPUT_ESCAPES_ROOT"] = "WORKSPACE_TARGET_OUTPUT_ESCAPES_ROOT";
|
|
12877
|
+
WorkspaceValidateCode2["WORKSPACE_DEPLOY_LANE_NO_ENVIRONMENT_SELECTION"] = "WORKSPACE_DEPLOY_LANE_NO_ENVIRONMENT_SELECTION";
|
|
12878
|
+
WorkspaceValidateCode2["WORKSPACE_DEPLOY_ENVIRONMENT_FILE_MISSING"] = "WORKSPACE_DEPLOY_ENVIRONMENT_FILE_MISSING";
|
|
12879
|
+
WorkspaceValidateCode2["WORKSPACE_DEPLOY_ENVIRONMENT_FILE_UNREFERENCED"] = "WORKSPACE_DEPLOY_ENVIRONMENT_FILE_UNREFERENCED";
|
|
12880
|
+
WorkspaceValidateCode2["WORKSPACE_DEPLOY_HOSTING_TARGET_MISSING"] = "WORKSPACE_DEPLOY_HOSTING_TARGET_MISSING";
|
|
12881
|
+
WorkspaceValidateCode2["WORKSPACE_DEPLOY_LANE_ASYMMETRIC"] = "WORKSPACE_DEPLOY_LANE_ASYMMETRIC";
|
|
12882
|
+
return WorkspaceValidateCode2;
|
|
12883
|
+
})(WorkspaceValidateCode || {});
|
|
12884
|
+
|
|
12885
|
+
// packages/dbx-cli/validate/src/lib/workspace-validate/format.ts
|
|
12886
|
+
function formatResult11(result) {
|
|
12887
|
+
return formatFileGroupedResult({
|
|
12888
|
+
title: "Workspace convention validation",
|
|
12889
|
+
summary: `Checked ${result.projectsChecked} project(s) and ${result.referencesChecked} target reference(s) across group(s) [${result.groups.join(", ")}].`,
|
|
12890
|
+
innerKey: (violation) => violation.project,
|
|
12891
|
+
result
|
|
12892
|
+
});
|
|
12893
|
+
}
|
|
12894
|
+
|
|
12895
|
+
// packages/dbx-cli/validate/src/lib/workspace-validate/inspect.ts
|
|
12896
|
+
import { readdir as readdir8, readFile as readFile11, stat as stat10 } from "node:fs/promises";
|
|
12897
|
+
import { basename as basename5, dirname as dirname2, join as join10, relative as relative6, resolve as resolve3 } from "node:path";
|
|
12898
|
+
|
|
12899
|
+
// packages/dbx-cli/validate/src/lib/workspace-validate/references.ts
|
|
12900
|
+
var NX_SUBCOMMANDS = /* @__PURE__ */ new Set([
|
|
12901
|
+
"run",
|
|
12902
|
+
"run-many",
|
|
12903
|
+
"affected",
|
|
12904
|
+
"affected:graph",
|
|
12905
|
+
"affected:test",
|
|
12906
|
+
"affected:build",
|
|
12907
|
+
"graph",
|
|
12908
|
+
"dep-graph",
|
|
12909
|
+
"print-affected",
|
|
12910
|
+
"show",
|
|
12911
|
+
"list",
|
|
12912
|
+
"report",
|
|
12913
|
+
"migrate",
|
|
12914
|
+
"reset",
|
|
12915
|
+
"repair",
|
|
12916
|
+
"generate",
|
|
12917
|
+
"g",
|
|
12918
|
+
"add",
|
|
12919
|
+
"init",
|
|
12920
|
+
"import",
|
|
12921
|
+
"sync",
|
|
12922
|
+
"release",
|
|
12923
|
+
"format",
|
|
12924
|
+
"format:check",
|
|
12925
|
+
"format:write",
|
|
12926
|
+
"daemon",
|
|
12927
|
+
"connect",
|
|
12928
|
+
"login",
|
|
12929
|
+
"logout",
|
|
12930
|
+
"view-logs",
|
|
12931
|
+
"exec",
|
|
12932
|
+
"watch",
|
|
12933
|
+
"documentation",
|
|
12934
|
+
"help",
|
|
12935
|
+
"register",
|
|
12936
|
+
"record",
|
|
12937
|
+
"start-ci-run"
|
|
12938
|
+
]);
|
|
12939
|
+
var INTERPOLATION_PATTERN = /[$`{}()*?]|\\\\/;
|
|
12940
|
+
var TOKEN_LEADING_CHARS = `"'`;
|
|
12941
|
+
var TOKEN_TRAILING_CHARS = `"',;`;
|
|
12942
|
+
var PROJECT_TOKEN_PATTERN = /^[a-zA-Z0-9@][a-zA-Z0-9@/_.-]*$/;
|
|
12943
|
+
var TARGET_TOKEN_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9_.:-]*$/;
|
|
12944
|
+
function isFlag(token) {
|
|
12945
|
+
return token.startsWith("-");
|
|
12946
|
+
}
|
|
12947
|
+
function isResolvable(token) {
|
|
12948
|
+
return token.length > 0 && !INTERPOLATION_PATTERN.test(token);
|
|
12949
|
+
}
|
|
12950
|
+
function trimTokenPunctuation(raw) {
|
|
12951
|
+
let start = 0;
|
|
12952
|
+
let end = raw.length;
|
|
12953
|
+
while (start < end && TOKEN_LEADING_CHARS.includes(raw[start])) {
|
|
12954
|
+
start += 1;
|
|
12955
|
+
}
|
|
12956
|
+
while (end > start && TOKEN_TRAILING_CHARS.includes(raw[end - 1])) {
|
|
12957
|
+
end -= 1;
|
|
12958
|
+
}
|
|
12959
|
+
return raw.slice(start, end);
|
|
12960
|
+
}
|
|
12961
|
+
function tokenize(text) {
|
|
12962
|
+
const out = [];
|
|
12963
|
+
for (const raw of text.split(/\s+/)) {
|
|
12964
|
+
const token = trimTokenPunctuation(raw);
|
|
12965
|
+
if (token.length > 0) {
|
|
12966
|
+
out.push(token);
|
|
12967
|
+
}
|
|
12968
|
+
}
|
|
12969
|
+
return out;
|
|
12970
|
+
}
|
|
12971
|
+
function isNxBinary(token) {
|
|
12972
|
+
return token === "nx" || token.endsWith("/nx");
|
|
12973
|
+
}
|
|
12974
|
+
function changesToForeignDirectory(tokens) {
|
|
12975
|
+
let result = false;
|
|
12976
|
+
for (let i = 0; i < tokens.length - 1 && !result; i += 1) {
|
|
12977
|
+
if (tokens[i] === "cd") {
|
|
12978
|
+
const destination = tokens[i + 1];
|
|
12979
|
+
result = destination.startsWith("~") || destination.startsWith("/");
|
|
12980
|
+
}
|
|
12981
|
+
}
|
|
12982
|
+
return result;
|
|
12983
|
+
}
|
|
12984
|
+
function parseExplicitForm(tokens, runIndex) {
|
|
12985
|
+
let result;
|
|
12986
|
+
for (let i = runIndex + 1; i < tokens.length; i += 1) {
|
|
12987
|
+
const token = tokens[i];
|
|
12988
|
+
if (isFlag(token)) {
|
|
12989
|
+
continue;
|
|
12990
|
+
}
|
|
12991
|
+
if (isResolvable(token) && token.includes(":")) {
|
|
12992
|
+
const [project, target, configuration] = token.split(":");
|
|
12993
|
+
if (project && target && PROJECT_TOKEN_PATTERN.test(project) && TARGET_TOKEN_PATTERN.test(target)) {
|
|
12994
|
+
result = { project, target, configuration: configuration || void 0, raw: token };
|
|
12995
|
+
}
|
|
12996
|
+
}
|
|
12997
|
+
break;
|
|
12998
|
+
}
|
|
12999
|
+
return result;
|
|
13000
|
+
}
|
|
13001
|
+
function parseShorthandForm(tokens, nxIndex) {
|
|
13002
|
+
const positional = [];
|
|
13003
|
+
for (let i = nxIndex + 1; i < tokens.length && positional.length < 2; i += 1) {
|
|
13004
|
+
const token = tokens[i];
|
|
13005
|
+
if (!isFlag(token)) {
|
|
13006
|
+
positional.push(token);
|
|
13007
|
+
}
|
|
13008
|
+
}
|
|
13009
|
+
let result;
|
|
13010
|
+
const [target, project] = positional;
|
|
13011
|
+
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)) {
|
|
13012
|
+
result = { project, target, raw: `${target} ${project}` };
|
|
13013
|
+
}
|
|
13014
|
+
return result;
|
|
13015
|
+
}
|
|
13016
|
+
function scanTargetReferences(input) {
|
|
13017
|
+
const { text, sourceFile, sourceTarget, line } = input;
|
|
13018
|
+
const references = [];
|
|
13019
|
+
const tokens = tokenize(text);
|
|
13020
|
+
if (changesToForeignDirectory(tokens)) {
|
|
13021
|
+
return references;
|
|
13022
|
+
}
|
|
13023
|
+
for (let i = 0; i < tokens.length; i += 1) {
|
|
13024
|
+
if (!isNxBinary(tokens[i])) {
|
|
13025
|
+
continue;
|
|
13026
|
+
}
|
|
13027
|
+
let firstPositional;
|
|
13028
|
+
let firstPositionalIndex = -1;
|
|
13029
|
+
for (let j = i + 1; j < tokens.length; j += 1) {
|
|
13030
|
+
if (!isFlag(tokens[j])) {
|
|
13031
|
+
firstPositional = tokens[j];
|
|
13032
|
+
firstPositionalIndex = j;
|
|
13033
|
+
break;
|
|
13034
|
+
}
|
|
13035
|
+
}
|
|
13036
|
+
if (firstPositional === void 0) {
|
|
13037
|
+
continue;
|
|
13038
|
+
}
|
|
13039
|
+
if (firstPositional === "run") {
|
|
13040
|
+
const parsed = parseExplicitForm(tokens, firstPositionalIndex);
|
|
13041
|
+
if (parsed) {
|
|
13042
|
+
references.push({ project: parsed.project, target: parsed.target, configuration: parsed.configuration, sourceFile, line, sourceTarget, raw: parsed.raw });
|
|
13043
|
+
}
|
|
13044
|
+
} else if (!NX_SUBCOMMANDS.has(firstPositional)) {
|
|
13045
|
+
const parsed = parseShorthandForm(tokens, i);
|
|
13046
|
+
if (parsed) {
|
|
13047
|
+
references.push({ project: parsed.project, target: parsed.target, configuration: void 0, sourceFile, line, sourceTarget, raw: parsed.raw });
|
|
13048
|
+
}
|
|
13049
|
+
}
|
|
13050
|
+
}
|
|
13051
|
+
return references;
|
|
13052
|
+
}
|
|
13053
|
+
function stripComment(line) {
|
|
13054
|
+
const trimmed = line.trimStart();
|
|
13055
|
+
let result;
|
|
13056
|
+
if (trimmed.startsWith("#")) {
|
|
13057
|
+
result = "";
|
|
13058
|
+
} else {
|
|
13059
|
+
const index = line.indexOf(" #");
|
|
13060
|
+
result = index === -1 ? line : line.slice(0, index);
|
|
13061
|
+
}
|
|
13062
|
+
return result;
|
|
13063
|
+
}
|
|
13064
|
+
function scanDocumentTargetReferences(input) {
|
|
13065
|
+
const { text, sourceFile } = input;
|
|
13066
|
+
const references = [];
|
|
13067
|
+
const lines = text.split("\n");
|
|
13068
|
+
for (const [index, line] of lines.entries()) {
|
|
13069
|
+
for (const reference of scanTargetReferences({ text: stripComment(line), sourceFile, line: index + 1 })) {
|
|
13070
|
+
references.push(reference);
|
|
13071
|
+
}
|
|
13072
|
+
}
|
|
13073
|
+
return references;
|
|
13074
|
+
}
|
|
13075
|
+
function scanHostingTargets(command) {
|
|
13076
|
+
const out = [];
|
|
13077
|
+
const pattern = /hosting:([a-zA-Z0-9_-]+)/g;
|
|
13078
|
+
let match = pattern.exec(command);
|
|
13079
|
+
while (match !== null) {
|
|
13080
|
+
out.push(match[1]);
|
|
13081
|
+
match = pattern.exec(command);
|
|
13082
|
+
}
|
|
13083
|
+
return out;
|
|
13084
|
+
}
|
|
13085
|
+
|
|
13086
|
+
// packages/dbx-cli/validate/src/lib/workspace-validate/inspect.ts
|
|
13087
|
+
var SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set(["node_modules", "dist", "tmp", "coverage", ".nx", ".git", ".firebase", ".angular", ".cache", "templates"]);
|
|
13088
|
+
var MAX_WALK_DEPTH = 4;
|
|
13089
|
+
var PROJECT_CONFIG_FILENAME = "project.json";
|
|
13090
|
+
var ENVIRONMENTS_SUBPATH = join10("src", "environments");
|
|
13091
|
+
var ENVIRONMENT_FILE_PATTERN = /^environment\..+\.ts$/;
|
|
13092
|
+
var EXTRA_REFERENCE_SOURCES = [".circleci/config.yml", ".circleci/config.yaml", ".github/workflows"];
|
|
13093
|
+
async function pathExists(path) {
|
|
13094
|
+
let result;
|
|
13095
|
+
try {
|
|
13096
|
+
await stat10(path);
|
|
13097
|
+
result = true;
|
|
13098
|
+
} catch {
|
|
13099
|
+
result = false;
|
|
13100
|
+
}
|
|
13101
|
+
return result;
|
|
13102
|
+
}
|
|
13103
|
+
async function readJsonFile(path) {
|
|
13104
|
+
let result;
|
|
13105
|
+
try {
|
|
13106
|
+
const text = await readFile11(path, "utf8");
|
|
13107
|
+
const parsed = JSON.parse(text);
|
|
13108
|
+
result = parsed !== null && typeof parsed === "object" ? parsed : void 0;
|
|
13109
|
+
} catch {
|
|
13110
|
+
result = void 0;
|
|
13111
|
+
}
|
|
13112
|
+
return result;
|
|
13113
|
+
}
|
|
13114
|
+
async function collectProjectConfigPaths(config) {
|
|
13115
|
+
const { workspaceRoot, relativeDir, depth } = config;
|
|
13116
|
+
const collected = [];
|
|
13117
|
+
const absoluteDir = join10(workspaceRoot, relativeDir);
|
|
13118
|
+
const entries = await readdir8(absoluteDir, { withFileTypes: true }).catch(() => []);
|
|
13119
|
+
for (const entry of entries) {
|
|
13120
|
+
if (entry.isFile() && entry.name === PROJECT_CONFIG_FILENAME) {
|
|
13121
|
+
collected.push(relativeDir === "" ? PROJECT_CONFIG_FILENAME : join10(relativeDir, PROJECT_CONFIG_FILENAME));
|
|
13122
|
+
} else if (entry.isDirectory() && depth < MAX_WALK_DEPTH && !SKIPPED_DIRECTORIES.has(entry.name) && !entry.name.startsWith("firebase-export-")) {
|
|
13123
|
+
const nested = await collectProjectConfigPaths({ workspaceRoot, relativeDir: relativeDir === "" ? entry.name : join10(relativeDir, entry.name), depth: depth + 1 });
|
|
13124
|
+
for (const path of nested) {
|
|
13125
|
+
collected.push(path);
|
|
13126
|
+
}
|
|
13127
|
+
}
|
|
13128
|
+
}
|
|
13129
|
+
return collected;
|
|
13130
|
+
}
|
|
13131
|
+
function extractCommands(options) {
|
|
13132
|
+
const collected = [];
|
|
13133
|
+
const single = options["command"];
|
|
13134
|
+
if (typeof single === "string") {
|
|
13135
|
+
collected.push(single);
|
|
13136
|
+
}
|
|
13137
|
+
const many = options["commands"];
|
|
13138
|
+
if (Array.isArray(many)) {
|
|
13139
|
+
for (const entry of many) {
|
|
13140
|
+
if (typeof entry === "string") {
|
|
13141
|
+
collected.push(entry);
|
|
13142
|
+
} else if (entry !== null && typeof entry === "object") {
|
|
13143
|
+
const command = entry["command"];
|
|
13144
|
+
if (typeof command === "string") {
|
|
13145
|
+
collected.push(command);
|
|
13146
|
+
}
|
|
13147
|
+
}
|
|
13148
|
+
}
|
|
13149
|
+
}
|
|
13150
|
+
return collected;
|
|
13151
|
+
}
|
|
13152
|
+
async function resolveFileReplacements(config) {
|
|
13153
|
+
const { workspaceRoot, raw } = config;
|
|
13154
|
+
const collected = [];
|
|
13155
|
+
if (Array.isArray(raw)) {
|
|
13156
|
+
for (const entry of raw) {
|
|
13157
|
+
if (entry === null || typeof entry !== "object") {
|
|
13158
|
+
continue;
|
|
13159
|
+
}
|
|
13160
|
+
const record = entry;
|
|
13161
|
+
const replace = record["replace"];
|
|
13162
|
+
const withPath = record["with"];
|
|
13163
|
+
if (typeof replace === "string" && typeof withPath === "string") {
|
|
13164
|
+
collected.push({
|
|
13165
|
+
replace,
|
|
13166
|
+
with: withPath,
|
|
13167
|
+
replaceExists: await pathExists(join10(workspaceRoot, replace)),
|
|
13168
|
+
withExists: await pathExists(join10(workspaceRoot, withPath))
|
|
13169
|
+
});
|
|
13170
|
+
}
|
|
13171
|
+
}
|
|
13172
|
+
}
|
|
13173
|
+
return collected;
|
|
13174
|
+
}
|
|
13175
|
+
async function resolveConfigurations(config) {
|
|
13176
|
+
const { workspaceRoot, raw } = config;
|
|
13177
|
+
const collected = [];
|
|
13178
|
+
if (raw !== null && typeof raw === "object") {
|
|
13179
|
+
for (const [name, value] of Object.entries(raw)) {
|
|
13180
|
+
const options = value !== null && typeof value === "object" ? value : {};
|
|
13181
|
+
const esbuildConfigValue = options["esbuildConfig"];
|
|
13182
|
+
const esbuildConfig = typeof esbuildConfigValue === "string" ? esbuildConfigValue : void 0;
|
|
13183
|
+
collected.push({
|
|
13184
|
+
name,
|
|
13185
|
+
fileReplacements: await resolveFileReplacements({ workspaceRoot, raw: options["fileReplacements"] }),
|
|
13186
|
+
esbuildConfig,
|
|
13187
|
+
esbuildConfigExists: esbuildConfig === void 0 ? false : await pathExists(join10(workspaceRoot, esbuildConfig))
|
|
13188
|
+
});
|
|
13189
|
+
}
|
|
13190
|
+
}
|
|
13191
|
+
return collected;
|
|
13192
|
+
}
|
|
13193
|
+
async function resolveTargets(config) {
|
|
13194
|
+
const { workspaceRoot, raw } = config;
|
|
13195
|
+
const collected = [];
|
|
13196
|
+
if (raw !== null && typeof raw === "object") {
|
|
13197
|
+
for (const [name, value] of Object.entries(raw)) {
|
|
13198
|
+
const target = value !== null && typeof value === "object" ? value : {};
|
|
13199
|
+
const executorValue = target["executor"];
|
|
13200
|
+
const defaultConfigurationValue = target["defaultConfiguration"];
|
|
13201
|
+
const optionsValue = target["options"];
|
|
13202
|
+
const options = optionsValue !== null && typeof optionsValue === "object" ? optionsValue : {};
|
|
13203
|
+
const outputsValue = target["outputs"];
|
|
13204
|
+
collected.push({
|
|
13205
|
+
name,
|
|
13206
|
+
executor: typeof executorValue === "string" ? executorValue : void 0,
|
|
13207
|
+
configurations: await resolveConfigurations({ workspaceRoot, raw: target["configurations"] }),
|
|
13208
|
+
defaultConfiguration: typeof defaultConfigurationValue === "string" ? defaultConfigurationValue : void 0,
|
|
13209
|
+
outputs: Array.isArray(outputsValue) ? outputsValue.filter((o) => typeof o === "string") : [],
|
|
13210
|
+
options,
|
|
13211
|
+
commands: extractCommands(options)
|
|
13212
|
+
});
|
|
13213
|
+
}
|
|
13214
|
+
}
|
|
13215
|
+
return collected;
|
|
13216
|
+
}
|
|
13217
|
+
async function collectEnvironmentFiles(config) {
|
|
13218
|
+
const { workspaceRoot, projectRoot } = config;
|
|
13219
|
+
const relativeDir = projectRoot === "" ? ENVIRONMENTS_SUBPATH : join10(projectRoot, ENVIRONMENTS_SUBPATH);
|
|
13220
|
+
const collected = [];
|
|
13221
|
+
const entries = await readdir8(join10(workspaceRoot, relativeDir), { withFileTypes: true }).catch(() => []);
|
|
13222
|
+
for (const entry of entries) {
|
|
13223
|
+
if (entry.isFile() && ENVIRONMENT_FILE_PATTERN.test(entry.name)) {
|
|
13224
|
+
collected.push(join10(relativeDir, entry.name));
|
|
13225
|
+
}
|
|
13226
|
+
}
|
|
13227
|
+
return collected.sort((a, b) => a.localeCompare(b));
|
|
13228
|
+
}
|
|
13229
|
+
var ENVIRONMENT_MENTION_PATTERN = /environment(?:\.[A-Za-z0-9_-]+)*\.ts/g;
|
|
13230
|
+
async function collectReferencedEnvironmentFiles(config) {
|
|
13231
|
+
const { workspaceRoot, targets, environmentFiles } = config;
|
|
13232
|
+
const referenced = /* @__PURE__ */ new Set();
|
|
13233
|
+
const esbuildConfigPaths = /* @__PURE__ */ new Set();
|
|
13234
|
+
for (const target of targets) {
|
|
13235
|
+
const baseEsbuildConfig = target.options["esbuildConfig"];
|
|
13236
|
+
if (typeof baseEsbuildConfig === "string") {
|
|
13237
|
+
esbuildConfigPaths.add(baseEsbuildConfig);
|
|
13238
|
+
}
|
|
13239
|
+
for (const configuration of target.configurations) {
|
|
13240
|
+
for (const replacement of configuration.fileReplacements) {
|
|
13241
|
+
referenced.add(replacement.with);
|
|
13242
|
+
}
|
|
13243
|
+
if (configuration.esbuildConfig !== void 0) {
|
|
13244
|
+
esbuildConfigPaths.add(configuration.esbuildConfig);
|
|
13245
|
+
}
|
|
13246
|
+
}
|
|
13247
|
+
}
|
|
13248
|
+
const mentionedBasenames = /* @__PURE__ */ new Set();
|
|
13249
|
+
for (const configPath of esbuildConfigPaths) {
|
|
13250
|
+
const text = await readFile11(join10(workspaceRoot, configPath), "utf8").catch(() => void 0);
|
|
13251
|
+
if (text === void 0) {
|
|
13252
|
+
continue;
|
|
13253
|
+
}
|
|
13254
|
+
for (const match of text.matchAll(ENVIRONMENT_MENTION_PATTERN)) {
|
|
13255
|
+
mentionedBasenames.add(match[0]);
|
|
13256
|
+
}
|
|
13257
|
+
}
|
|
13258
|
+
for (const environmentFile of environmentFiles) {
|
|
13259
|
+
if (mentionedBasenames.has(basename5(environmentFile))) {
|
|
13260
|
+
referenced.add(environmentFile);
|
|
13261
|
+
}
|
|
13262
|
+
}
|
|
13263
|
+
return Array.from(referenced).sort((a, b) => a.localeCompare(b));
|
|
13264
|
+
}
|
|
13265
|
+
async function inspectProject(config) {
|
|
13266
|
+
const { workspaceRoot, configFile } = config;
|
|
13267
|
+
const parsed = await readJsonFile(join10(workspaceRoot, configFile));
|
|
13268
|
+
if (parsed === void 0) {
|
|
13269
|
+
return void 0;
|
|
13270
|
+
}
|
|
13271
|
+
const projectRoot = dirname2(configFile) === "." ? "" : dirname2(configFile);
|
|
13272
|
+
const nameValue = parsed["name"];
|
|
13273
|
+
const fallbackNameDir = projectRoot === "" ? workspaceRoot : projectRoot;
|
|
13274
|
+
const name = typeof nameValue === "string" && nameValue.length > 0 ? nameValue : basename5(fallbackNameDir);
|
|
13275
|
+
const targets = await resolveTargets({ workspaceRoot, raw: parsed["targets"] });
|
|
13276
|
+
const environmentFiles = await collectEnvironmentFiles({ workspaceRoot, projectRoot });
|
|
13277
|
+
return {
|
|
13278
|
+
name,
|
|
13279
|
+
root: projectRoot,
|
|
13280
|
+
configFile,
|
|
13281
|
+
targets,
|
|
13282
|
+
environmentFiles,
|
|
13283
|
+
referencedEnvironmentFiles: await collectReferencedEnvironmentFiles({ workspaceRoot, targets, environmentFiles })
|
|
13284
|
+
};
|
|
13285
|
+
}
|
|
13286
|
+
async function inspectNxConfig(workspaceRoot) {
|
|
13287
|
+
const parsed = await readJsonFile(join10(workspaceRoot, "nx.json"));
|
|
13288
|
+
const targetDefaultNames = [];
|
|
13289
|
+
const plugins = [];
|
|
13290
|
+
const targetDefaults = parsed?.["targetDefaults"];
|
|
13291
|
+
if (targetDefaults !== null && typeof targetDefaults === "object") {
|
|
13292
|
+
for (const key of Object.keys(targetDefaults)) {
|
|
13293
|
+
if (!key.includes(":")) {
|
|
13294
|
+
targetDefaultNames.push(key);
|
|
13295
|
+
}
|
|
13296
|
+
}
|
|
13297
|
+
}
|
|
13298
|
+
const pluginsValue = parsed?.["plugins"];
|
|
13299
|
+
if (Array.isArray(pluginsValue)) {
|
|
13300
|
+
for (const entry of pluginsValue) {
|
|
13301
|
+
if (typeof entry === "string") {
|
|
13302
|
+
plugins.push(entry);
|
|
13303
|
+
} else if (entry !== null && typeof entry === "object") {
|
|
13304
|
+
const plugin = entry["plugin"];
|
|
13305
|
+
if (typeof plugin === "string") {
|
|
13306
|
+
plugins.push(plugin);
|
|
13307
|
+
}
|
|
13308
|
+
}
|
|
13309
|
+
}
|
|
13310
|
+
}
|
|
13311
|
+
return { targetDefaultNames, plugins };
|
|
13312
|
+
}
|
|
13313
|
+
async function inspectFirebaseConfig(workspaceRoot) {
|
|
13314
|
+
const parsed = await readJsonFile(join10(workspaceRoot, "firebase.json"));
|
|
13315
|
+
if (parsed === void 0) {
|
|
13316
|
+
return { present: false, hostingTargets: [] };
|
|
13317
|
+
}
|
|
13318
|
+
const hostingValue = parsed["hosting"];
|
|
13319
|
+
const singleHostingEntry = hostingValue !== null && typeof hostingValue === "object" ? [hostingValue] : [];
|
|
13320
|
+
const entries = Array.isArray(hostingValue) ? hostingValue : singleHostingEntry;
|
|
13321
|
+
const hostingTargets = [];
|
|
13322
|
+
for (const entry of entries) {
|
|
13323
|
+
if (entry === null || typeof entry !== "object") {
|
|
13324
|
+
continue;
|
|
13325
|
+
}
|
|
13326
|
+
const record = entry;
|
|
13327
|
+
const target = record["target"];
|
|
13328
|
+
const site = record["site"];
|
|
13329
|
+
if (typeof target === "string") {
|
|
13330
|
+
hostingTargets.push(target);
|
|
13331
|
+
} else if (typeof site === "string") {
|
|
13332
|
+
hostingTargets.push(site);
|
|
13333
|
+
}
|
|
13334
|
+
}
|
|
13335
|
+
return { present: true, hostingTargets };
|
|
13336
|
+
}
|
|
13337
|
+
async function collectExtraReferenceSources(workspaceRoot) {
|
|
13338
|
+
const collected = [];
|
|
13339
|
+
for (const candidate of EXTRA_REFERENCE_SOURCES) {
|
|
13340
|
+
const absolute = join10(workspaceRoot, candidate);
|
|
13341
|
+
let stats;
|
|
13342
|
+
try {
|
|
13343
|
+
stats = await stat10(absolute);
|
|
13344
|
+
} catch {
|
|
13345
|
+
continue;
|
|
13346
|
+
}
|
|
13347
|
+
if (stats.isFile()) {
|
|
13348
|
+
collected.push(candidate);
|
|
13349
|
+
} else if (stats.isDirectory()) {
|
|
13350
|
+
const entries = await readdir8(absolute, { withFileTypes: true });
|
|
13351
|
+
for (const entry of entries) {
|
|
13352
|
+
if (entry.isFile() && /\.ya?ml$/.test(entry.name)) {
|
|
13353
|
+
collected.push(join10(candidate, entry.name));
|
|
13354
|
+
}
|
|
13355
|
+
}
|
|
13356
|
+
}
|
|
13357
|
+
}
|
|
13358
|
+
const rootEntries = await readdir8(workspaceRoot, { withFileTypes: true }).catch(() => []);
|
|
13359
|
+
for (const entry of rootEntries) {
|
|
13360
|
+
if (entry.isFile() && entry.name.endsWith(".sh")) {
|
|
13361
|
+
collected.push(entry.name);
|
|
13362
|
+
}
|
|
13363
|
+
}
|
|
13364
|
+
return collected.sort((a, b) => a.localeCompare(b));
|
|
13365
|
+
}
|
|
13366
|
+
async function collectPackageScriptReferences(workspaceRoot) {
|
|
13367
|
+
const parsed = await readJsonFile(join10(workspaceRoot, "package.json"));
|
|
13368
|
+
const scripts = parsed?.["scripts"];
|
|
13369
|
+
const collected = [];
|
|
13370
|
+
if (scripts !== null && typeof scripts === "object") {
|
|
13371
|
+
for (const [scriptName, value] of Object.entries(scripts)) {
|
|
13372
|
+
if (typeof value === "string") {
|
|
13373
|
+
for (const reference of scanTargetReferences({ text: value, sourceFile: "package.json", sourceTarget: scriptName })) {
|
|
13374
|
+
collected.push(reference);
|
|
13375
|
+
}
|
|
13376
|
+
}
|
|
13377
|
+
}
|
|
13378
|
+
}
|
|
13379
|
+
return collected;
|
|
13380
|
+
}
|
|
13381
|
+
async function inspectWorkspace(input) {
|
|
13382
|
+
const workspaceRoot = resolve3(input.workspaceRoot);
|
|
13383
|
+
const configPaths = await collectProjectConfigPaths({ workspaceRoot, relativeDir: "", depth: 0 });
|
|
13384
|
+
const projects = [];
|
|
13385
|
+
const references = [];
|
|
13386
|
+
for (const configFile of [...configPaths].sort((a, b) => a.localeCompare(b))) {
|
|
13387
|
+
const project = await inspectProject({ workspaceRoot, configFile });
|
|
13388
|
+
if (project === void 0) {
|
|
13389
|
+
continue;
|
|
13390
|
+
}
|
|
13391
|
+
projects.push(project);
|
|
13392
|
+
for (const target of project.targets) {
|
|
13393
|
+
for (const command of target.commands) {
|
|
13394
|
+
for (const reference of scanTargetReferences({ text: command, sourceFile: configFile, sourceTarget: target.name })) {
|
|
13395
|
+
references.push(reference);
|
|
13396
|
+
}
|
|
13397
|
+
}
|
|
13398
|
+
}
|
|
13399
|
+
}
|
|
13400
|
+
for (const sourceFile of await collectExtraReferenceSources(workspaceRoot)) {
|
|
13401
|
+
const text = await readFile11(join10(workspaceRoot, sourceFile), "utf8").catch(() => void 0);
|
|
13402
|
+
if (text !== void 0) {
|
|
13403
|
+
for (const reference of scanDocumentTargetReferences({ text, sourceFile })) {
|
|
13404
|
+
references.push(reference);
|
|
13405
|
+
}
|
|
13406
|
+
}
|
|
13407
|
+
}
|
|
13408
|
+
for (const reference of await collectPackageScriptReferences(workspaceRoot)) {
|
|
13409
|
+
references.push(reference);
|
|
13410
|
+
}
|
|
13411
|
+
return {
|
|
13412
|
+
workspaceRoot,
|
|
13413
|
+
projects,
|
|
13414
|
+
references,
|
|
13415
|
+
firebase: await inspectFirebaseConfig(workspaceRoot),
|
|
13416
|
+
nx: await inspectNxConfig(workspaceRoot)
|
|
13417
|
+
};
|
|
13418
|
+
}
|
|
13419
|
+
function pathEscapesWorkspaceRoot(config) {
|
|
13420
|
+
const { workspaceRoot, candidate } = config;
|
|
13421
|
+
const resolved = resolve3(workspaceRoot, candidate);
|
|
13422
|
+
const rel = relative6(workspaceRoot, resolved);
|
|
13423
|
+
return rel.startsWith("..");
|
|
13424
|
+
}
|
|
13425
|
+
|
|
13426
|
+
// packages/dbx-cli/validate/src/lib/workspace-validate/types.ts
|
|
13427
|
+
var WORKSPACE_RULE_GROUPS = ["targets", "deploy"];
|
|
13428
|
+
var DEPLOY_TARGET_PREFIX = "ci-deploy-";
|
|
13429
|
+
|
|
13430
|
+
// packages/dbx-cli/validate/src/lib/workspace-validate/rules.ts
|
|
13431
|
+
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"]);
|
|
13432
|
+
var ESBUILD_CONFIG_EXECUTORS = /* @__PURE__ */ new Set(["@nx/esbuild:esbuild"]);
|
|
13433
|
+
var PLUGIN_INFERRED_TARGETS = [
|
|
13434
|
+
{ plugin: "@nx/eslint/plugin", targets: ["lint"] },
|
|
13435
|
+
{ plugin: "@nx/vitest", targets: ["test", "test-ci"] },
|
|
13436
|
+
{ plugin: "@nx/vite", targets: ["build", "serve", "preview", "test"] },
|
|
13437
|
+
{ plugin: "@nx/jest", targets: ["test"] },
|
|
13438
|
+
{ plugin: "@nx/webpack/plugin", targets: ["build", "serve", "preview"] },
|
|
13439
|
+
{ plugin: "@nx/rollup/plugin", targets: ["build"] },
|
|
13440
|
+
{ plugin: "@nx/js/typescript", targets: ["typecheck", "build"] },
|
|
13441
|
+
{ plugin: "@nx/playwright/plugin", targets: ["e2e", "e2e-ci"] },
|
|
13442
|
+
{ plugin: "@nx/cypress/plugin", targets: ["e2e", "component-test"] },
|
|
13443
|
+
{ plugin: "@nx/next/plugin", targets: ["build", "dev", "start", "serve-static"] }
|
|
13444
|
+
];
|
|
13445
|
+
var FILE_EXTENSION_PATTERN = /\.(?:ts|tsx|js|mjs|cjs|json|sh|ya?ml|md|html|scss|css)$/;
|
|
13446
|
+
var DEPLOY_LANE_PATTERN = new RegExp(`^${DEPLOY_TARGET_PREFIX}([a-z0-9]+)$`);
|
|
13447
|
+
function pushViolation11(buffer, violation) {
|
|
13448
|
+
const severity = violation.severity ?? "error";
|
|
13449
|
+
const filled = {
|
|
13450
|
+
code: violation.code,
|
|
13451
|
+
severity,
|
|
13452
|
+
message: violation.message,
|
|
13453
|
+
group: violation.group,
|
|
13454
|
+
project: violation.project,
|
|
13455
|
+
file: violation.file,
|
|
13456
|
+
line: violation.line,
|
|
13457
|
+
remediation: attachRemediation(violation.code)
|
|
13458
|
+
};
|
|
13459
|
+
buffer.push(filled);
|
|
13460
|
+
}
|
|
13461
|
+
function looksLikeProjectName(token) {
|
|
13462
|
+
return !token.includes("/") && !token.includes("\\") && !FILE_EXTENSION_PATTERN.test(token);
|
|
13463
|
+
}
|
|
13464
|
+
function isExemptTargetName(config) {
|
|
13465
|
+
const { targetName, nx } = config;
|
|
13466
|
+
let result = nx.targetDefaultNames.includes(targetName);
|
|
13467
|
+
if (!result) {
|
|
13468
|
+
const plugins = new Set(nx.plugins);
|
|
13469
|
+
result = PLUGIN_INFERRED_TARGETS.some((entry) => plugins.has(entry.plugin) && entry.targets.includes(targetName));
|
|
13470
|
+
}
|
|
13471
|
+
return result;
|
|
13472
|
+
}
|
|
13473
|
+
function substituteOutputTokens(config) {
|
|
13474
|
+
const { entry, options, projectRoot } = config;
|
|
13475
|
+
let unresolved = false;
|
|
13476
|
+
const withOptions = entry.replaceAll(/\{options\.(\w+)\}/g, (_match, key) => {
|
|
13477
|
+
const value = options[key];
|
|
13478
|
+
let replacement;
|
|
13479
|
+
if (typeof value === "string") {
|
|
13480
|
+
replacement = value;
|
|
13481
|
+
} else if (typeof value === "number" || typeof value === "boolean") {
|
|
13482
|
+
replacement = String(value);
|
|
13483
|
+
} else {
|
|
13484
|
+
unresolved = true;
|
|
13485
|
+
replacement = "";
|
|
13486
|
+
}
|
|
13487
|
+
return replacement;
|
|
13488
|
+
});
|
|
13489
|
+
const projectRootValue = projectRoot === "" ? "." : projectRoot;
|
|
13490
|
+
return unresolved ? void 0 : withOptions.replaceAll("{workspaceRoot}", ".").replaceAll("{projectRoot}", projectRootValue);
|
|
13491
|
+
}
|
|
13492
|
+
function environmentSelectionState(config) {
|
|
13493
|
+
const { executor, configuration } = config;
|
|
13494
|
+
let result;
|
|
13495
|
+
if (executor !== void 0 && FILE_REPLACEMENT_EXECUTORS.has(executor)) {
|
|
13496
|
+
result = configuration.fileReplacements.length > 0 ? "ok" : "missing";
|
|
13497
|
+
} else if (executor !== void 0 && ESBUILD_CONFIG_EXECUTORS.has(executor)) {
|
|
13498
|
+
result = configuration.esbuildConfig === void 0 ? "missing" : "ok";
|
|
13499
|
+
} else {
|
|
13500
|
+
result = "unknown";
|
|
13501
|
+
}
|
|
13502
|
+
return result;
|
|
13503
|
+
}
|
|
13504
|
+
function environmentSelectionOptionName(executor) {
|
|
13505
|
+
return executor !== void 0 && ESBUILD_CONFIG_EXECUTORS.has(executor) ? "esbuildConfig" : "fileReplacements";
|
|
13506
|
+
}
|
|
13507
|
+
function indexTargets(project) {
|
|
13508
|
+
const map = /* @__PURE__ */ new Map();
|
|
13509
|
+
for (const target of project.targets) {
|
|
13510
|
+
map.set(target.name, target);
|
|
13511
|
+
}
|
|
13512
|
+
return map;
|
|
13513
|
+
}
|
|
13514
|
+
function checkTargetReferences(config) {
|
|
13515
|
+
const { inspection, targetsByProject, violations } = config;
|
|
13516
|
+
const group = "targets";
|
|
13517
|
+
for (const reference of inspection.references) {
|
|
13518
|
+
const targets = targetsByProject.get(reference.project);
|
|
13519
|
+
if (targets === void 0) {
|
|
13520
|
+
if (looksLikeProjectName(reference.project)) {
|
|
13521
|
+
pushViolation11(violations, {
|
|
13522
|
+
code: "WORKSPACE_TARGET_REF_PROJECT_MISSING",
|
|
13523
|
+
message: `\`${reference.raw}\` references project \`${reference.project}\`, which the workspace does not declare.`,
|
|
13524
|
+
group,
|
|
13525
|
+
project: reference.project,
|
|
13526
|
+
file: reference.sourceFile,
|
|
13527
|
+
line: reference.line
|
|
13528
|
+
});
|
|
13529
|
+
}
|
|
13530
|
+
continue;
|
|
13531
|
+
}
|
|
13532
|
+
const target = targets.get(reference.target);
|
|
13533
|
+
if (target === void 0) {
|
|
13534
|
+
if (!isExemptTargetName({ targetName: reference.target, nx: inspection.nx })) {
|
|
13535
|
+
pushViolation11(violations, {
|
|
13536
|
+
code: "WORKSPACE_TARGET_REF_TARGET_MISSING",
|
|
13537
|
+
message: `\`${reference.raw}\` references target \`${reference.target}\` on project \`${reference.project}\`, which declares no such target.`,
|
|
13538
|
+
group,
|
|
13539
|
+
project: reference.project,
|
|
13540
|
+
file: reference.sourceFile,
|
|
13541
|
+
line: reference.line
|
|
13542
|
+
});
|
|
13543
|
+
}
|
|
13544
|
+
continue;
|
|
13545
|
+
}
|
|
13546
|
+
if (reference.configuration !== void 0 && target.configurations.length > 0 && !target.configurations.some((configuration) => configuration.name === reference.configuration)) {
|
|
13547
|
+
const declared = target.configurations.map((configuration) => configuration.name).join(", ");
|
|
13548
|
+
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\`)`;
|
|
13549
|
+
pushViolation11(violations, {
|
|
13550
|
+
code: "WORKSPACE_TARGET_REF_CONFIGURATION_MISSING",
|
|
13551
|
+
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}.`,
|
|
13552
|
+
group,
|
|
13553
|
+
project: reference.project,
|
|
13554
|
+
file: reference.sourceFile,
|
|
13555
|
+
line: reference.line
|
|
13556
|
+
});
|
|
13557
|
+
}
|
|
13558
|
+
}
|
|
13559
|
+
}
|
|
13560
|
+
function checkTargetOutputs(config) {
|
|
13561
|
+
const { inspection, violations } = config;
|
|
13562
|
+
const group = "targets";
|
|
13563
|
+
for (const project of inspection.projects) {
|
|
13564
|
+
for (const target of project.targets) {
|
|
13565
|
+
for (const entry of target.outputs) {
|
|
13566
|
+
const substituted = substituteOutputTokens({ entry, options: target.options, projectRoot: project.root });
|
|
13567
|
+
if (substituted === void 0) {
|
|
13568
|
+
continue;
|
|
13569
|
+
}
|
|
13570
|
+
if (pathEscapesWorkspaceRoot({ workspaceRoot: inspection.workspaceRoot, candidate: substituted })) {
|
|
13571
|
+
pushViolation11(violations, {
|
|
13572
|
+
code: "WORKSPACE_TARGET_OUTPUT_ESCAPES_ROOT",
|
|
13573
|
+
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.`,
|
|
13574
|
+
group,
|
|
13575
|
+
project: project.name,
|
|
13576
|
+
file: project.configFile,
|
|
13577
|
+
line: void 0
|
|
13578
|
+
});
|
|
13579
|
+
}
|
|
13580
|
+
}
|
|
13581
|
+
}
|
|
13582
|
+
}
|
|
13583
|
+
}
|
|
13584
|
+
function checkDeployLaneEnvironmentSelection(config) {
|
|
13585
|
+
const { inspection, targetsByProject, violations } = config;
|
|
13586
|
+
const group = "deploy";
|
|
13587
|
+
for (const project of inspection.projects) {
|
|
13588
|
+
const targets = targetsByProject.get(project.name);
|
|
13589
|
+
if (targets === void 0) {
|
|
13590
|
+
continue;
|
|
13591
|
+
}
|
|
13592
|
+
const deployTargetNames = new Set(project.targets.filter((target) => target.name.startsWith(DEPLOY_TARGET_PREFIX)).map((target) => target.name));
|
|
13593
|
+
for (const reference of inspection.references) {
|
|
13594
|
+
if (reference.sourceFile !== project.configFile || reference.sourceTarget === void 0 || !deployTargetNames.has(reference.sourceTarget) || reference.project !== project.name || reference.configuration === void 0) {
|
|
13595
|
+
continue;
|
|
13596
|
+
}
|
|
13597
|
+
const buildTarget = targets.get(reference.target);
|
|
13598
|
+
const configuration = buildTarget?.configurations.find((entry) => entry.name === reference.configuration);
|
|
13599
|
+
if (buildTarget === void 0 || configuration === void 0) {
|
|
13600
|
+
continue;
|
|
13601
|
+
}
|
|
13602
|
+
if (environmentSelectionState({ executor: buildTarget.executor, configuration }) === "missing") {
|
|
13603
|
+
pushViolation11(violations, {
|
|
13604
|
+
code: "WORKSPACE_DEPLOY_LANE_NO_ENVIRONMENT_SELECTION",
|
|
13605
|
+
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\`.`,
|
|
13606
|
+
group,
|
|
13607
|
+
project: project.name,
|
|
13608
|
+
file: project.configFile,
|
|
13609
|
+
line: void 0
|
|
13610
|
+
});
|
|
13611
|
+
}
|
|
13612
|
+
}
|
|
13613
|
+
}
|
|
13614
|
+
}
|
|
13615
|
+
function checkConfigurationPaths(config) {
|
|
13616
|
+
const { inspection, violations } = config;
|
|
13617
|
+
const group = "deploy";
|
|
13618
|
+
for (const project of inspection.projects) {
|
|
13619
|
+
for (const target of project.targets) {
|
|
13620
|
+
for (const configuration of target.configurations) {
|
|
13621
|
+
for (const replacement of configuration.fileReplacements) {
|
|
13622
|
+
if (!replacement.replaceExists) {
|
|
13623
|
+
pushViolation11(violations, {
|
|
13624
|
+
code: "WORKSPACE_DEPLOY_ENVIRONMENT_FILE_MISSING",
|
|
13625
|
+
message: `\`${project.name}:${target.name}:${configuration.name}\` replaces \`${replacement.replace}\`, which does not exist.`,
|
|
13626
|
+
group,
|
|
13627
|
+
project: project.name,
|
|
13628
|
+
file: project.configFile,
|
|
13629
|
+
line: void 0
|
|
13630
|
+
});
|
|
13631
|
+
}
|
|
13632
|
+
if (!replacement.withExists) {
|
|
13633
|
+
pushViolation11(violations, {
|
|
13634
|
+
code: "WORKSPACE_DEPLOY_ENVIRONMENT_FILE_MISSING",
|
|
13635
|
+
message: `\`${project.name}:${target.name}:${configuration.name}\` replaces with \`${replacement.with}\`, which does not exist.`,
|
|
13636
|
+
group,
|
|
13637
|
+
project: project.name,
|
|
13638
|
+
file: project.configFile,
|
|
13639
|
+
line: void 0
|
|
13640
|
+
});
|
|
13641
|
+
}
|
|
13642
|
+
}
|
|
13643
|
+
if (configuration.esbuildConfig !== void 0 && !configuration.esbuildConfigExists) {
|
|
13644
|
+
pushViolation11(violations, {
|
|
13645
|
+
code: "WORKSPACE_DEPLOY_ENVIRONMENT_FILE_MISSING",
|
|
13646
|
+
message: `\`${project.name}:${target.name}:${configuration.name}\` sets \`esbuildConfig\` to \`${configuration.esbuildConfig}\`, which does not exist.`,
|
|
13647
|
+
group,
|
|
13648
|
+
project: project.name,
|
|
13649
|
+
file: project.configFile,
|
|
13650
|
+
line: void 0
|
|
13651
|
+
});
|
|
13652
|
+
}
|
|
13653
|
+
}
|
|
13654
|
+
}
|
|
13655
|
+
}
|
|
13656
|
+
}
|
|
13657
|
+
function checkUnreferencedEnvironmentFiles(config) {
|
|
13658
|
+
const { inspection, violations } = config;
|
|
13659
|
+
const group = "deploy";
|
|
13660
|
+
for (const project of inspection.projects) {
|
|
13661
|
+
const referenced = new Set(project.referencedEnvironmentFiles);
|
|
13662
|
+
for (const environmentFile of project.environmentFiles) {
|
|
13663
|
+
if (!referenced.has(environmentFile)) {
|
|
13664
|
+
pushViolation11(violations, {
|
|
13665
|
+
code: "WORKSPACE_DEPLOY_ENVIRONMENT_FILE_UNREFERENCED",
|
|
13666
|
+
message: `\`${environmentFile}\` is not selected by any build configuration of \`${project.name}\` \u2014 it is edited and reviewed but never compiled.`,
|
|
13667
|
+
severity: "warning",
|
|
13668
|
+
group,
|
|
13669
|
+
project: project.name,
|
|
13670
|
+
file: project.configFile,
|
|
13671
|
+
line: void 0
|
|
13672
|
+
});
|
|
13673
|
+
}
|
|
13674
|
+
}
|
|
13675
|
+
}
|
|
13676
|
+
}
|
|
13677
|
+
function checkHostingTargets(config) {
|
|
13678
|
+
const { inspection, violations } = config;
|
|
13679
|
+
const group = "deploy";
|
|
13680
|
+
if (!inspection.firebase.present) {
|
|
13681
|
+
return;
|
|
13682
|
+
}
|
|
13683
|
+
const declared = new Set(inspection.firebase.hostingTargets);
|
|
13684
|
+
for (const project of inspection.projects) {
|
|
13685
|
+
for (const target of project.targets) {
|
|
13686
|
+
for (const command of target.commands) {
|
|
13687
|
+
for (const hostingTarget of scanHostingTargets(command)) {
|
|
13688
|
+
if (!declared.has(hostingTarget)) {
|
|
13689
|
+
pushViolation11(violations, {
|
|
13690
|
+
code: "WORKSPACE_DEPLOY_HOSTING_TARGET_MISSING",
|
|
13691
|
+
message: `\`${project.name}:${target.name}\` deploys \`hosting:${hostingTarget}\`, which \`firebase.json\` does not declare (declared: [${Array.from(declared).join(", ") || "none"}]).`,
|
|
13692
|
+
group,
|
|
13693
|
+
project: project.name,
|
|
13694
|
+
file: project.configFile,
|
|
13695
|
+
line: void 0
|
|
13696
|
+
});
|
|
13697
|
+
}
|
|
13698
|
+
}
|
|
13699
|
+
}
|
|
13700
|
+
}
|
|
13701
|
+
}
|
|
13702
|
+
}
|
|
13703
|
+
function checkDeployLaneSymmetry(config) {
|
|
13704
|
+
const { inspection, violations } = config;
|
|
13705
|
+
const group = "deploy";
|
|
13706
|
+
const lanesByProject = /* @__PURE__ */ new Map();
|
|
13707
|
+
const allLanes = /* @__PURE__ */ new Set();
|
|
13708
|
+
for (const project of inspection.projects) {
|
|
13709
|
+
const lanes = /* @__PURE__ */ new Set();
|
|
13710
|
+
for (const target of project.targets) {
|
|
13711
|
+
const match = DEPLOY_LANE_PATTERN.exec(target.name);
|
|
13712
|
+
if (match !== null) {
|
|
13713
|
+
lanes.add(match[1]);
|
|
13714
|
+
allLanes.add(match[1]);
|
|
13715
|
+
}
|
|
13716
|
+
}
|
|
13717
|
+
if (lanes.size > 0) {
|
|
13718
|
+
lanesByProject.set(project.name, lanes);
|
|
13719
|
+
}
|
|
13720
|
+
}
|
|
13721
|
+
if (lanesByProject.size < 2) {
|
|
13722
|
+
return;
|
|
13723
|
+
}
|
|
13724
|
+
for (const project of inspection.projects) {
|
|
13725
|
+
const lanes = lanesByProject.get(project.name);
|
|
13726
|
+
if (lanes === void 0) {
|
|
13727
|
+
continue;
|
|
13728
|
+
}
|
|
13729
|
+
for (const lane of Array.from(allLanes).sort((a, b) => a.localeCompare(b))) {
|
|
13730
|
+
if (!lanes.has(lane)) {
|
|
13731
|
+
pushViolation11(violations, {
|
|
13732
|
+
code: "WORKSPACE_DEPLOY_LANE_ASYMMETRIC",
|
|
13733
|
+
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}\`.`,
|
|
13734
|
+
severity: "warning",
|
|
13735
|
+
group,
|
|
13736
|
+
project: project.name,
|
|
13737
|
+
file: project.configFile,
|
|
13738
|
+
line: void 0
|
|
13739
|
+
});
|
|
13740
|
+
}
|
|
13741
|
+
}
|
|
13742
|
+
}
|
|
13743
|
+
}
|
|
13744
|
+
function runRules9(input) {
|
|
13745
|
+
const { inspection, groups } = input;
|
|
13746
|
+
const violations = [];
|
|
13747
|
+
const targetsByProject = /* @__PURE__ */ new Map();
|
|
13748
|
+
for (const project of inspection.projects) {
|
|
13749
|
+
targetsByProject.set(project.name, indexTargets(project));
|
|
13750
|
+
}
|
|
13751
|
+
if (groups.includes("targets")) {
|
|
13752
|
+
checkTargetReferences({ inspection, targetsByProject, violations });
|
|
13753
|
+
checkTargetOutputs({ inspection, violations });
|
|
13754
|
+
}
|
|
13755
|
+
if (groups.includes("deploy")) {
|
|
13756
|
+
checkDeployLaneEnvironmentSelection({ inspection, targetsByProject, violations });
|
|
13757
|
+
checkConfigurationPaths({ inspection, violations });
|
|
13758
|
+
checkUnreferencedEnvironmentFiles({ inspection, violations });
|
|
13759
|
+
checkHostingTargets({ inspection, violations });
|
|
13760
|
+
checkDeployLaneSymmetry({ inspection, violations });
|
|
13761
|
+
}
|
|
13762
|
+
return violations;
|
|
13763
|
+
}
|
|
13764
|
+
|
|
13765
|
+
// packages/dbx-cli/validate/src/lib/workspace-validate/validate.ts
|
|
13766
|
+
function validateWorkspace(input) {
|
|
13767
|
+
const { inspection } = input;
|
|
13768
|
+
const groups = input.groups === void 0 || input.groups.length === 0 ? WORKSPACE_RULE_GROUPS : input.groups;
|
|
13769
|
+
const violations = runRules9({ inspection, groups });
|
|
13770
|
+
let errorCount = 0;
|
|
13771
|
+
let warningCount = 0;
|
|
13772
|
+
for (const violation of violations) {
|
|
13773
|
+
if (violation.severity === "error") {
|
|
13774
|
+
errorCount += 1;
|
|
13775
|
+
} else {
|
|
13776
|
+
warningCount += 1;
|
|
13777
|
+
}
|
|
13778
|
+
}
|
|
13779
|
+
return {
|
|
13780
|
+
violations,
|
|
13781
|
+
errorCount,
|
|
13782
|
+
warningCount,
|
|
13783
|
+
projectsChecked: inspection.projects.length,
|
|
13784
|
+
referencesChecked: inspection.references.length,
|
|
13785
|
+
groups
|
|
13786
|
+
};
|
|
13787
|
+
}
|
|
13788
|
+
|
|
12742
13789
|
// packages/dbx-cli/validate/src/lib/model-list-component/index.ts
|
|
12743
13790
|
var model_list_component_exports = {};
|
|
12744
13791
|
__export(model_list_component_exports, {
|
|
@@ -12749,8 +13796,8 @@ __export(model_list_component_exports, {
|
|
|
12749
13796
|
});
|
|
12750
13797
|
|
|
12751
13798
|
// packages/dbx-cli/model-test/src/lib/fixture/inspect.ts
|
|
12752
|
-
import { readFile as
|
|
12753
|
-
import { join as
|
|
13799
|
+
import { readFile as readFile12 } from "node:fs/promises";
|
|
13800
|
+
import { join as join11 } from "node:path";
|
|
12754
13801
|
|
|
12755
13802
|
// packages/dbx-cli/model-test/src/lib/fixture/extract.ts
|
|
12756
13803
|
import { Node as Node16, Project as Project9, SyntaxKind as SyntaxKind7 } from "ts-morph";
|
|
@@ -13247,21 +14294,21 @@ function collectIdentityImports(sourceFile) {
|
|
|
13247
14294
|
// packages/dbx-cli/model-test/src/lib/fixture/inspect.ts
|
|
13248
14295
|
var FIXTURE_RELATIVE_PATH = "src/test/fixture.ts";
|
|
13249
14296
|
async function inspectAppFixtures(apiAbs, apiRel) {
|
|
13250
|
-
const absolutePath =
|
|
14297
|
+
const absolutePath = join11(apiAbs, FIXTURE_RELATIVE_PATH);
|
|
13251
14298
|
const relativePath = `${apiRel}/${FIXTURE_RELATIVE_PATH}`;
|
|
13252
|
-
const text = await
|
|
14299
|
+
const text = await readFile12(absolutePath, "utf8");
|
|
13253
14300
|
return extractAppFixturesFromText({ text, fixturePath: relativePath });
|
|
13254
14301
|
}
|
|
13255
14302
|
|
|
13256
14303
|
// packages/dbx-cli/validate/src/lib/model-list-component/extract.ts
|
|
13257
|
-
import { readdir as
|
|
13258
|
-
import { basename as
|
|
14304
|
+
import { readdir as readdir9, readFile as readFile13, stat as stat11 } from "node:fs/promises";
|
|
14305
|
+
import { basename as basename6, join as join12, relative as relative7, sep as sep7 } from "node:path";
|
|
13259
14306
|
import { Node as Node17, Project as Project10 } from "ts-morph";
|
|
13260
14307
|
var MODEL_SUBPATH = "src/lib/model";
|
|
13261
14308
|
var MODEL_SUBPATH_PREFIX = `${MODEL_SUBPATH}/`;
|
|
13262
14309
|
var RESERVED_NAMES = RESERVED_MODEL_FOLDERS.map((r) => r.name);
|
|
13263
14310
|
async function extractComponentModels(componentAbs) {
|
|
13264
|
-
const modelRoot =
|
|
14311
|
+
const modelRoot = join12(componentAbs, MODEL_SUBPATH);
|
|
13265
14312
|
const folders = await listFolders(modelRoot);
|
|
13266
14313
|
if (folders === void 0) {
|
|
13267
14314
|
const result2 = { modelRoot, models: [], skipped: [], unidentifiedFolders: [] };
|
|
@@ -13351,13 +14398,13 @@ function projectRich(model, folder) {
|
|
|
13351
14398
|
}
|
|
13352
14399
|
async function fallbackForFolder(input) {
|
|
13353
14400
|
const { modelRoot, componentAbs, folder, project } = input;
|
|
13354
|
-
const folderAbs =
|
|
14401
|
+
const folderAbs = join12(modelRoot, folder);
|
|
13355
14402
|
const mainFile = await readMainFile(folderAbs, folder);
|
|
13356
14403
|
if (mainFile === void 0) return void 0;
|
|
13357
14404
|
const sourceFile = project.createSourceFile(mainFile.fileName, mainFile.text, { overwrite: true });
|
|
13358
14405
|
const identity = findIdentityIn(sourceFile);
|
|
13359
14406
|
if (!identity) return void 0;
|
|
13360
|
-
const sourceFileRel =
|
|
14407
|
+
const sourceFileRel = relative7(componentAbs, mainFile.absPath).split(sep7).join("/");
|
|
13361
14408
|
return {
|
|
13362
14409
|
folder,
|
|
13363
14410
|
modelName: deriveModelName(identity.constName, folder),
|
|
@@ -13373,13 +14420,13 @@ async function listFolders(modelRoot) {
|
|
|
13373
14420
|
let result;
|
|
13374
14421
|
let isDir = false;
|
|
13375
14422
|
try {
|
|
13376
|
-
const stats = await
|
|
14423
|
+
const stats = await stat11(modelRoot);
|
|
13377
14424
|
isDir = stats.isDirectory();
|
|
13378
14425
|
} catch {
|
|
13379
14426
|
isDir = false;
|
|
13380
14427
|
}
|
|
13381
14428
|
if (isDir) {
|
|
13382
|
-
const entries = await
|
|
14429
|
+
const entries = await readdir9(modelRoot, { withFileTypes: true });
|
|
13383
14430
|
const out = [];
|
|
13384
14431
|
for (const entry of entries) {
|
|
13385
14432
|
if (entry.isDirectory()) out.push(entry.name);
|
|
@@ -13390,11 +14437,11 @@ async function listFolders(modelRoot) {
|
|
|
13390
14437
|
return result;
|
|
13391
14438
|
}
|
|
13392
14439
|
async function readMainFile(folderAbs, folder) {
|
|
13393
|
-
const candidate =
|
|
14440
|
+
const candidate = join12(folderAbs, `${folder}.ts`);
|
|
13394
14441
|
let result;
|
|
13395
14442
|
try {
|
|
13396
|
-
const text = await
|
|
13397
|
-
result = { absPath: candidate, fileName:
|
|
14443
|
+
const text = await readFile13(candidate, "utf8");
|
|
14444
|
+
result = { absPath: candidate, fileName: basename6(candidate), text };
|
|
13398
14445
|
} catch {
|
|
13399
14446
|
result = void 0;
|
|
13400
14447
|
}
|
|
@@ -13539,21 +14586,21 @@ __export(model_api_list_app_exports, {
|
|
|
13539
14586
|
});
|
|
13540
14587
|
|
|
13541
14588
|
// packages/dbx-cli/validate/src/lib/model-api-list-app/extract.ts
|
|
13542
|
-
import { readFile as
|
|
13543
|
-
import { join as
|
|
14589
|
+
import { readFile as readFile14 } from "node:fs/promises";
|
|
14590
|
+
import { join as join13, relative as relative8, sep as sep8 } from "node:path";
|
|
13544
14591
|
var API_SUFFIX2 = ".api.ts";
|
|
13545
14592
|
var MODEL_SUBPATH2 = "src/lib";
|
|
13546
14593
|
async function extractApiList(input) {
|
|
13547
|
-
const modelRoot =
|
|
14594
|
+
const modelRoot = join13(input.componentAbs, MODEL_SUBPATH2);
|
|
13548
14595
|
const apiFiles = await collectFilesWithSuffix(modelRoot, API_SUFFIX2);
|
|
13549
14596
|
const entries = [];
|
|
13550
14597
|
const files = [];
|
|
13551
14598
|
for (const filePath of apiFiles) {
|
|
13552
|
-
const text = await
|
|
14599
|
+
const text = await readFile14(filePath, "utf8");
|
|
13553
14600
|
if (!text.includes("callModelFirebaseFunctionMapFactory")) {
|
|
13554
14601
|
continue;
|
|
13555
14602
|
}
|
|
13556
|
-
const sourceFileRel =
|
|
14603
|
+
const sourceFileRel = relative8(input.componentAbs, filePath).split(sep8).join("/");
|
|
13557
14604
|
const extraction = extractCrudEntries({ name: sourceFileRel, text });
|
|
13558
14605
|
const fileEntries = extraction.entries.map((entry) => ({ ...entry, sourceFile: sourceFileRel }));
|
|
13559
14606
|
const counts = countVerbs(fileEntries);
|
|
@@ -13684,17 +14731,17 @@ __export(model_api_lookup_exports, {
|
|
|
13684
14731
|
});
|
|
13685
14732
|
|
|
13686
14733
|
// packages/dbx-cli/validate/src/lib/model-api-lookup/extract.ts
|
|
13687
|
-
import { readFile as
|
|
13688
|
-
import { join as
|
|
14734
|
+
import { readFile as readFile16 } from "node:fs/promises";
|
|
14735
|
+
import { join as join15, relative as relative10, sep as sep10 } from "node:path";
|
|
13689
14736
|
|
|
13690
14737
|
// packages/dbx-cli/validate/src/lib/model-api-lookup/extract-actions.ts
|
|
13691
|
-
import { readFile as
|
|
13692
|
-
import { join as
|
|
14738
|
+
import { readFile as readFile15 } from "node:fs/promises";
|
|
14739
|
+
import { join as join14, relative as relative9, sep as sep9 } from "node:path";
|
|
13693
14740
|
import { Project as Project11, SyntaxKind as SyntaxKind8 } from "ts-morph";
|
|
13694
14741
|
var ACTION_SERVER_SUFFIX = ".action.server.ts";
|
|
13695
14742
|
var COMMON_MODEL_SUBPATH = "src/app/common/model";
|
|
13696
14743
|
async function buildActionLookup(apiAbs) {
|
|
13697
|
-
const modelRoot =
|
|
14744
|
+
const modelRoot = join14(apiAbs, COMMON_MODEL_SUBPATH);
|
|
13698
14745
|
const files = await collectFilesWithSuffix(modelRoot, ACTION_SERVER_SUFFIX);
|
|
13699
14746
|
const methodsByParams = /* @__PURE__ */ new Map();
|
|
13700
14747
|
const factoriesByParams = /* @__PURE__ */ new Map();
|
|
@@ -13703,9 +14750,9 @@ async function buildActionLookup(apiAbs) {
|
|
|
13703
14750
|
}
|
|
13704
14751
|
const project = new Project11({ useInMemoryFileSystem: true, skipAddingFilesFromTsConfig: true });
|
|
13705
14752
|
for (const filePath of files) {
|
|
13706
|
-
const text = await
|
|
14753
|
+
const text = await readFile15(filePath, "utf8");
|
|
13707
14754
|
const sourceFile = project.createSourceFile(filePath, text, { overwrite: true });
|
|
13708
|
-
const sourceFileRel =
|
|
14755
|
+
const sourceFileRel = relative9(apiAbs, filePath).split(sep9).join("/");
|
|
13709
14756
|
collectAbstractMethods({ sourceFile, sourceFileRel, methodsByParams });
|
|
13710
14757
|
collectFactoryFunctions({ sourceFile, sourceFileRel, factoriesByParams });
|
|
13711
14758
|
}
|
|
@@ -13865,13 +14912,13 @@ function mapDocFields(fields) {
|
|
|
13865
14912
|
return result;
|
|
13866
14913
|
}
|
|
13867
14914
|
async function collectApiSources(componentAbs) {
|
|
13868
|
-
const root =
|
|
14915
|
+
const root = join15(componentAbs, COMPONENT_LIB_SUBPATH2);
|
|
13869
14916
|
const files = await collectFilesWithSuffix(root, API_SUFFIX3);
|
|
13870
14917
|
const sources = [];
|
|
13871
14918
|
for (const fileAbs of files) {
|
|
13872
|
-
const text = await
|
|
14919
|
+
const text = await readFile16(fileAbs, "utf8");
|
|
13873
14920
|
if (!text.includes("callModelFirebaseFunctionMapFactory")) continue;
|
|
13874
|
-
const fileRel =
|
|
14921
|
+
const fileRel = relative10(componentAbs, fileAbs).split(sep10).join("/");
|
|
13875
14922
|
sources.push({ fileAbs, fileRel, text });
|
|
13876
14923
|
}
|
|
13877
14924
|
return sources;
|
|
@@ -14019,14 +15066,14 @@ __export(server_actions_list_app_exports, {
|
|
|
14019
15066
|
});
|
|
14020
15067
|
|
|
14021
15068
|
// packages/dbx-cli/validate/src/lib/server-actions-list-app/extract.ts
|
|
14022
|
-
import { readdir as
|
|
14023
|
-
import { dirname as
|
|
15069
|
+
import { readdir as readdir10, readFile as readFile17 } from "node:fs/promises";
|
|
15070
|
+
import { dirname as dirname3, join as join16, relative as relative11, sep as sep11 } from "node:path";
|
|
14024
15071
|
import { Node as Node18, Project as Project12 } from "ts-morph";
|
|
14025
15072
|
var ACTION_SERVER_SUFFIX2 = ".action.server.ts";
|
|
14026
15073
|
var COMMON_MODEL_SUBPATH2 = "src/app/common/model";
|
|
14027
15074
|
var COMMON_BARREL_REL = "src/app/common/index.ts";
|
|
14028
15075
|
async function extractServerActions(apiAbs, apiRel) {
|
|
14029
|
-
const modelRoot =
|
|
15076
|
+
const modelRoot = join16(apiAbs, COMMON_MODEL_SUBPATH2);
|
|
14030
15077
|
const actionFiles = await collectFilesWithSuffix(modelRoot, ACTION_SERVER_SUFFIX2);
|
|
14031
15078
|
if (actionFiles.length === 0) {
|
|
14032
15079
|
return { modelRoot, entries: [], fixtureStatus: "ok" };
|
|
@@ -14046,14 +15093,14 @@ async function extractServerActions(apiAbs, apiRel) {
|
|
|
14046
15093
|
}
|
|
14047
15094
|
const entries = [];
|
|
14048
15095
|
for (const filePath of actionFiles) {
|
|
14049
|
-
const text = await
|
|
15096
|
+
const text = await readFile17(filePath, "utf8");
|
|
14050
15097
|
const sourceFile = project.createSourceFile(filePath, text, { overwrite: true });
|
|
14051
15098
|
const classes = findServerActionClasses(sourceFile);
|
|
14052
15099
|
for (const className of classes) {
|
|
14053
15100
|
const wiring = await inspectSiblingModule(filePath, className);
|
|
14054
15101
|
const expectedGetterName = camelize(className);
|
|
14055
15102
|
const fixtureCoverage = fixtureStatus === "ok" ? fixtureCoverageMap.get(expectedGetterName) ?? unmatchedFixtureCoverage(expectedGetterName) : void 0;
|
|
14056
|
-
const sourceFileRel =
|
|
15103
|
+
const sourceFileRel = relative11(apiAbs, filePath).split(sep11).join("/");
|
|
14057
15104
|
entries.push({
|
|
14058
15105
|
className,
|
|
14059
15106
|
sourceFile: sourceFileRel,
|
|
@@ -14095,14 +15142,14 @@ async function inspectSiblingModule(actionFilePath, className) {
|
|
|
14095
15142
|
return { modulePath: moduleCandidates[0], providedByModule: false, exportedByModule: false };
|
|
14096
15143
|
}
|
|
14097
15144
|
async function findModuleCandidates(actionFilePath) {
|
|
14098
|
-
const dir =
|
|
15145
|
+
const dir = dirname3(actionFilePath);
|
|
14099
15146
|
const out = [];
|
|
14100
15147
|
try {
|
|
14101
|
-
const entries = await
|
|
15148
|
+
const entries = await readdir10(dir, { withFileTypes: true });
|
|
14102
15149
|
for (const entry of entries) {
|
|
14103
15150
|
if (!entry.isFile()) continue;
|
|
14104
15151
|
if (!entry.name.endsWith(".module.ts")) continue;
|
|
14105
|
-
out.push(
|
|
15152
|
+
out.push(join16(dir, entry.name));
|
|
14106
15153
|
}
|
|
14107
15154
|
} catch {
|
|
14108
15155
|
}
|
|
@@ -14112,7 +15159,7 @@ async function findModuleCandidates(actionFilePath) {
|
|
|
14112
15159
|
async function safeRead(path) {
|
|
14113
15160
|
let result;
|
|
14114
15161
|
try {
|
|
14115
|
-
result = await
|
|
15162
|
+
result = await readFile17(path, "utf8");
|
|
14116
15163
|
} catch {
|
|
14117
15164
|
result = void 0;
|
|
14118
15165
|
}
|
|
@@ -14159,7 +15206,7 @@ function moduleArrayContains(decoratorArg, propertyName, className) {
|
|
|
14159
15206
|
return false;
|
|
14160
15207
|
}
|
|
14161
15208
|
async function readCommonBarrelExports(apiAbs) {
|
|
14162
|
-
const path =
|
|
15209
|
+
const path = join16(apiAbs, COMMON_BARREL_REL);
|
|
14163
15210
|
const text = await safeRead(path);
|
|
14164
15211
|
if (text === void 0) return /* @__PURE__ */ new Set();
|
|
14165
15212
|
const project = new Project12({ useInMemoryFileSystem: true });
|
|
@@ -14338,8 +15385,19 @@ function formatReportAsJson4(report) {
|
|
|
14338
15385
|
|
|
14339
15386
|
// packages/dbx-cli/validate/src/lib/dbx-asset-list-app/format.markdown.ts
|
|
14340
15387
|
function formatReportAsMarkdown4(report) {
|
|
14341
|
-
const
|
|
14342
|
-
const lines = [
|
|
15388
|
+
const basename7 = report.componentDir.split("/").pop() ?? report.componentDir;
|
|
15389
|
+
const lines = [
|
|
15390
|
+
`# App assets \u2014 ${basename7}`,
|
|
15391
|
+
"",
|
|
15392
|
+
`Component: \`${report.componentDir}\``,
|
|
15393
|
+
`App: \`${report.apiDir}\``,
|
|
15394
|
+
"",
|
|
15395
|
+
`\`src/lib/assets.ts\`: ${formatBool(report.assetsFileExists)}`,
|
|
15396
|
+
`Barrel re-exports \`./assets\`: ${formatBool(report.barrelReExportsAssets)}`,
|
|
15397
|
+
`\`provideDbxAssetLoader()\` called in \`src/root.app.config.ts\`: ${formatBool(report.providerWiredInApp)}`,
|
|
15398
|
+
"",
|
|
15399
|
+
`## Assets (${report.assets.length})`
|
|
15400
|
+
];
|
|
14343
15401
|
if (report.assets.length === 0) {
|
|
14344
15402
|
lines.push("", "_None found._");
|
|
14345
15403
|
} else {
|
|
@@ -14527,11 +15585,11 @@ function formatReportAsJson5(report) {
|
|
|
14527
15585
|
|
|
14528
15586
|
// packages/dbx-cli/validate/src/lib/notification-m-list-app/format.markdown.ts
|
|
14529
15587
|
function formatReportAsMarkdown5(report) {
|
|
14530
|
-
const
|
|
15588
|
+
const basename7 = report.componentDir.split("/").pop() ?? report.componentDir;
|
|
14531
15589
|
const aggregatorRecordText = report.aggregatorRecordName ? code(report.aggregatorRecordName) : "_Not defined._";
|
|
14532
15590
|
const templateConfigsFactoryText = report.templateConfigsArrayFactoryName ? code(report.templateConfigsArrayFactoryName) : "_Not defined._";
|
|
14533
15591
|
const lines = [
|
|
14534
|
-
`# App notifications \u2014 ${
|
|
15592
|
+
`# App notifications \u2014 ${basename7}`,
|
|
14535
15593
|
"",
|
|
14536
15594
|
`Component: \`${report.componentDir}\``,
|
|
14537
15595
|
`API: \`${report.apiDir}\``,
|
|
@@ -14728,9 +15786,20 @@ function formatReportAsJson6(report) {
|
|
|
14728
15786
|
|
|
14729
15787
|
// packages/dbx-cli/validate/src/lib/storagefile-m-list-app/format.markdown.ts
|
|
14730
15788
|
function formatReportAsMarkdown6(report) {
|
|
14731
|
-
const
|
|
15789
|
+
const basename7 = report.componentDir.split("/").pop() ?? report.componentDir;
|
|
14732
15790
|
const uploadServiceFactoryText = report.uploadServiceFactoryName ? code2(report.uploadServiceFactoryName) : "_Not defined._";
|
|
14733
|
-
const lines = [
|
|
15791
|
+
const lines = [
|
|
15792
|
+
`# App storagefiles \u2014 ${basename7}`,
|
|
15793
|
+
"",
|
|
15794
|
+
`Component: \`${report.componentDir}\``,
|
|
15795
|
+
`API: \`${report.apiDir}\``,
|
|
15796
|
+
"",
|
|
15797
|
+
`Upload service factory: ${uploadServiceFactoryText}`,
|
|
15798
|
+
`Wired via \`StorageFileInitializeFromUploadService\` provider: ${formatBool3(report.uploadServiceWiredInApi)}`,
|
|
15799
|
+
`Processing handler call present: ${formatBool3(report.processingHandlerWiredInApi)}`,
|
|
15800
|
+
"",
|
|
15801
|
+
`## Purposes (${report.purposes.length})`
|
|
15802
|
+
];
|
|
14734
15803
|
if (report.purposes.length === 0) {
|
|
14735
15804
|
lines.push("", "_None found._");
|
|
14736
15805
|
} else {
|
|
@@ -14914,8 +15983,8 @@ __export(dbx_color_template_list_app_exports, {
|
|
|
14914
15983
|
});
|
|
14915
15984
|
|
|
14916
15985
|
// packages/dbx-cli/validate/src/lib/dbx-color-template-list-app/inspect.ts
|
|
14917
|
-
import { readFile as
|
|
14918
|
-
import { join as
|
|
15986
|
+
import { readFile as readFile18 } from "node:fs/promises";
|
|
15987
|
+
import { join as join17, relative as relative12, sep as sep12 } from "node:path";
|
|
14919
15988
|
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"];
|
|
14920
15989
|
async function inspectColorTemplates(apiAbs, apiRel) {
|
|
14921
15990
|
const present = [];
|
|
@@ -14925,7 +15994,7 @@ async function inspectColorTemplates(apiAbs, apiRel) {
|
|
|
14925
15994
|
for (const candidate of candidates) {
|
|
14926
15995
|
const text = await readFileIfExists2(candidate);
|
|
14927
15996
|
if (text === void 0) continue;
|
|
14928
|
-
const rel =
|
|
15997
|
+
const rel = relative12(apiAbs, candidate).split(sep12).join("/");
|
|
14929
15998
|
present.push({ relPath: rel, text });
|
|
14930
15999
|
}
|
|
14931
16000
|
} catch (err) {
|
|
@@ -14942,25 +16011,25 @@ async function inspectColorTemplates(apiAbs, apiRel) {
|
|
|
14942
16011
|
async function collectCandidatePaths(apiAbs) {
|
|
14943
16012
|
const explicit = [];
|
|
14944
16013
|
for (const rel of CANDIDATE_RELPATHS) {
|
|
14945
|
-
explicit.push(
|
|
16014
|
+
explicit.push(join17(apiAbs, rel));
|
|
14946
16015
|
}
|
|
14947
|
-
const scanned = await scanProviderFiles(
|
|
16016
|
+
const scanned = await scanProviderFiles(join17(apiAbs, "src"));
|
|
14948
16017
|
return [...explicit, ...scanned];
|
|
14949
16018
|
}
|
|
14950
16019
|
async function scanProviderFiles(srcAbs) {
|
|
14951
|
-
const { readdir:
|
|
16020
|
+
const { readdir: readdir11 } = await import("node:fs/promises");
|
|
14952
16021
|
const out = [];
|
|
14953
16022
|
const stack = [srcAbs];
|
|
14954
16023
|
while (stack.length > 0) {
|
|
14955
16024
|
const current = stack.pop();
|
|
14956
16025
|
let entries;
|
|
14957
16026
|
try {
|
|
14958
|
-
entries = await
|
|
16027
|
+
entries = await readdir11(current, { withFileTypes: true });
|
|
14959
16028
|
} catch {
|
|
14960
16029
|
continue;
|
|
14961
16030
|
}
|
|
14962
16031
|
for (const entry of entries) {
|
|
14963
|
-
const full =
|
|
16032
|
+
const full = join17(current, entry.name);
|
|
14964
16033
|
if (entry.isDirectory()) {
|
|
14965
16034
|
stack.push(full);
|
|
14966
16035
|
continue;
|
|
@@ -14975,7 +16044,7 @@ async function scanProviderFiles(srcAbs) {
|
|
|
14975
16044
|
async function readFileIfExists2(absPath) {
|
|
14976
16045
|
let result;
|
|
14977
16046
|
try {
|
|
14978
|
-
result = await
|
|
16047
|
+
result = await readFile18(absPath, "utf8");
|
|
14979
16048
|
} catch (err) {
|
|
14980
16049
|
const code3 = err.code;
|
|
14981
16050
|
if (code3 !== "ENOENT" && code3 !== "ENOTDIR" && code3 !== "EISDIR") {
|
|
@@ -15679,11 +16748,12 @@ function matchKindForRadius(exact, raw) {
|
|
|
15679
16748
|
if (exact.defaults.light === raw || exact.defaults.dark === raw) return "(exact-match design-system corner token).";
|
|
15680
16749
|
return "(idiomatic full / pill radius \u2014 design system token).";
|
|
15681
16750
|
}
|
|
15682
|
-
function findExactRoleToken(
|
|
16751
|
+
function findExactRoleToken(params) {
|
|
16752
|
+
const { registry, raw, role, isAllowed } = params;
|
|
15683
16753
|
const candidates = registry.byRole.get(role) ?? [];
|
|
15684
16754
|
let result;
|
|
15685
16755
|
for (const entry of candidates) {
|
|
15686
|
-
if (entry.defaults.light === raw || entry.defaults.dark === raw) {
|
|
16756
|
+
if ((entry.defaults.light === raw || entry.defaults.dark === raw) && (isAllowed === void 0 || isAllowed(entry))) {
|
|
15687
16757
|
result = entry;
|
|
15688
16758
|
break;
|
|
15689
16759
|
}
|
|
@@ -15828,7 +16898,7 @@ var hardcodedRadius = (input) => {
|
|
|
15828
16898
|
const colonIndex = input.scss.indexOf(":", match.index);
|
|
15829
16899
|
if (colonIndex !== -1 && isInsideScssVarDeclaration(input.scss, colonIndex)) continue;
|
|
15830
16900
|
const raw = match[1];
|
|
15831
|
-
let exact = findExactRoleToken(input.tokenRegistry, raw, "radius");
|
|
16901
|
+
let exact = findExactRoleToken({ registry: input.tokenRegistry, raw, role: "radius" });
|
|
15832
16902
|
if (exact === void 0 && FULL_RADIUS_SYNONYMS.has(raw)) {
|
|
15833
16903
|
exact = input.tokenRegistry.findByCssVariable("--mat-sys-corner-full");
|
|
15834
16904
|
}
|
|
@@ -15859,7 +16929,7 @@ var hardcodedShadow = (input) => {
|
|
|
15859
16929
|
if (match.index === void 0) continue;
|
|
15860
16930
|
const raw = match[1].trim();
|
|
15861
16931
|
if (raw.startsWith("var(") || raw === "none") continue;
|
|
15862
|
-
const exact = findExactRoleToken(input.tokenRegistry, raw, "elevation");
|
|
16932
|
+
const exact = findExactRoleToken({ registry: input.tokenRegistry, raw, role: "elevation" });
|
|
15863
16933
|
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.`;
|
|
15864
16934
|
matches.push({
|
|
15865
16935
|
id: "hardcoded-shadow",
|
|
@@ -15904,12 +16974,13 @@ var hardcodedHintColor = (input) => {
|
|
|
15904
16974
|
return matches;
|
|
15905
16975
|
};
|
|
15906
16976
|
var HARDCODED_PADDING_RE = /(?:padding|margin)\s*(?:-(?:top|right|bottom|left))?\s*:\s*(\d+(?:\.\d+)?px)(?!\s*\))/gi;
|
|
16977
|
+
var DBX_PADDING_SCALE_RE = /^--dbx-padding-\d+$/;
|
|
15907
16978
|
var hardcodedPadding = (input) => {
|
|
15908
16979
|
const matches = [];
|
|
15909
16980
|
for (const match of input.scss.matchAll(HARDCODED_PADDING_RE)) {
|
|
15910
16981
|
if (match.index === void 0) continue;
|
|
15911
16982
|
const raw = match[1];
|
|
15912
|
-
const exact = findExactRoleToken(input.tokenRegistry, raw, "spacing");
|
|
16983
|
+
const exact = findExactRoleToken({ registry: input.tokenRegistry, raw, role: "spacing", isAllowed: (entry) => DBX_PADDING_SCALE_RE.test(entry.cssVariable) });
|
|
15913
16984
|
if (exact !== void 0) {
|
|
15914
16985
|
const utility = exact.utilityClasses?.[0];
|
|
15915
16986
|
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");
|
|
@@ -16647,5 +17718,6 @@ export {
|
|
|
16647
17718
|
storagefile_m_validate_folder_exports as storagefileValidateFolder,
|
|
16648
17719
|
system_m_list_app_exports as systemListApp,
|
|
16649
17720
|
system_m_validate_folder_exports as systemValidateFolder,
|
|
16650
|
-
ui_smell_check_exports as uiSmellCheck
|
|
17721
|
+
ui_smell_check_exports as uiSmellCheck,
|
|
17722
|
+
workspace_validate_exports as workspaceValidate
|
|
16651
17723
|
};
|