@adaptabletools/adaptable-mcp 0.1.0-canary.0 → 0.1.0-canary.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +58 -61
- package/data/changes/21-22.json +542 -0
- package/data/changes/22-23.json +639 -0
- package/data/changes/23-24.json +1874 -0
- package/data/manifest.snapshot.json +202 -0
- package/data/templates/angular/src/app/adaptableOptions.ts +40 -0
- package/data/templates/angular/src/app/agGridModules.ts +7 -0
- package/data/templates/angular/src/app/app.component.ts +66 -0
- package/data/templates/angular/src/app/app.module.ts +14 -0
- package/data/templates/angular/src/app/columnDefs.ts +121 -0
- package/data/templates/angular/src/app/gridOptions.ts +32 -0
- package/data/templates/angular/src/app/onAdaptableReady.ts +11 -0
- package/data/templates/angular/src/app/rowData.ts +851 -0
- package/data/templates/angular/src/app/styles.css +50 -0
- package/data/templates/angular/src/index.html +17 -0
- package/data/templates/angular/src/main.ts +18 -0
- package/data/templates/angular/src/polyfills.ts +1 -0
- package/data/templates/angular/tsconfig.json +16 -0
- package/data/templates/manifest.json +406 -0
- package/data/templates/react/src/App.tsx +71 -0
- package/data/templates/react/src/adaptableOptions.ts +40 -0
- package/data/templates/react/src/agGridModules.ts +7 -0
- package/data/templates/react/src/columnDefs.ts +121 -0
- package/data/templates/react/src/gridOptions.ts +32 -0
- package/data/templates/react/src/index.tsx +8 -0
- package/data/templates/react/src/onAdaptableReady.ts +11 -0
- package/data/templates/react/src/rowData.ts +851 -0
- package/data/templates/react/src/styles.css +50 -0
- package/data/templates/react/tsconfig.json +9 -0
- package/data/templates/vanilla/index.html +18 -0
- package/data/templates/vanilla/src/adaptableOptions.ts +40 -0
- package/data/templates/vanilla/src/agGridModules.ts +7 -0
- package/data/templates/vanilla/src/columnDefs.ts +121 -0
- package/data/templates/vanilla/src/gridOptions.ts +32 -0
- package/data/templates/vanilla/src/index.ts +41 -0
- package/data/templates/vanilla/src/onAdaptableReady.ts +11 -0
- package/data/templates/vanilla/src/rowData.ts +851 -0
- package/data/templates/vanilla/src/styles.css +50 -0
- package/data/templates/vanilla/tsconfig.json +14 -0
- package/data/templates/vue/.babelrc +3 -0
- package/data/templates/vue/public/index.html +19 -0
- package/data/templates/vue/src/App.vue +35 -0
- package/data/templates/vue/src/adaptableOptions.ts +40 -0
- package/data/templates/vue/src/agGridModules.ts +7 -0
- package/data/templates/vue/src/columnDefs.ts +121 -0
- package/data/templates/vue/src/gridOptions.ts +32 -0
- package/data/templates/vue/src/main.ts +6 -0
- package/data/templates/vue/src/onAdaptableReady.ts +11 -0
- package/data/templates/vue/src/rowData.ts +851 -0
- package/data/templates/vue/src/shims-vue.d.ts +6 -0
- package/data/templates/vue/src/styles.css +50 -0
- package/data/templates/vue/tsconfig.json +30 -0
- package/dist/bin.js +23 -0
- package/dist/bin.js.map +1 -0
- package/dist/cli/build-commands.js +200 -0
- package/dist/cli/build-commands.js.map +1 -0
- package/dist/cli/index.js +94 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/render.js +57 -0
- package/dist/cli/render.js.map +1 -0
- package/dist/core/anonymise/anonymise.js +315 -0
- package/dist/core/anonymise/anonymise.js.map +1 -0
- package/dist/core/anonymise/capabilities.js +94 -0
- package/dist/core/anonymise/capabilities.js.map +1 -0
- package/dist/core/aql/capabilities.js +339 -0
- package/dist/core/aql/capabilities.js.map +1 -0
- package/dist/core/aql/check.js +204 -0
- package/dist/core/aql/check.js.map +1 -0
- package/dist/core/aql/engine.js +92 -0
- package/dist/core/aql/engine.js.map +1 -0
- package/dist/core/aql/functions.js +108 -0
- package/dist/core/aql/functions.js.map +1 -0
- package/dist/core/aql/migrate.js +62 -0
- package/dist/core/aql/migrate.js.map +1 -0
- package/dist/core/capabilities.js +105 -0
- package/dist/core/capabilities.js.map +1 -0
- package/dist/core/changes/capabilities.js +92 -0
- package/dist/core/changes/capabilities.js.map +1 -0
- package/dist/core/changes/changes.js +101 -0
- package/dist/core/changes/changes.js.map +1 -0
- package/dist/core/changes/grep.js +183 -0
- package/dist/core/changes/grep.js.map +1 -0
- package/dist/core/changes/records.js +172 -0
- package/dist/core/changes/records.js.map +1 -0
- package/dist/core/changes/render-md.js +101 -0
- package/dist/core/changes/render-md.js.map +1 -0
- package/dist/core/changes/with-docs.js +86 -0
- package/dist/core/changes/with-docs.js.map +1 -0
- package/dist/core/compat/capabilities.js +127 -0
- package/dist/core/compat/capabilities.js.map +1 -0
- package/dist/core/compat/manifest.js +203 -0
- package/dist/core/compat/manifest.js.map +1 -0
- package/dist/core/constants.js +38 -0
- package/dist/core/constants.js.map +1 -0
- package/dist/core/context/capabilities.js +106 -0
- package/dist/core/context/capabilities.js.map +1 -0
- package/dist/core/context/detect.js +370 -0
- package/dist/core/context/detect.js.map +1 -0
- package/dist/core/context/projects.js +159 -0
- package/dist/core/context/projects.js.map +1 -0
- package/dist/core/context/resolve.js +85 -0
- package/dist/core/context/resolve.js.map +1 -0
- package/dist/core/convert/capabilities.js +86 -0
- package/dist/core/convert/capabilities.js.map +1 -0
- package/dist/core/convert/coldefs.js +313 -0
- package/dist/core/convert/coldefs.js.map +1 -0
- package/dist/core/docs/areas.js +22 -0
- package/dist/core/docs/areas.js.map +1 -0
- package/dist/core/docs/cache.js +133 -0
- package/dist/core/docs/cache.js.map +1 -0
- package/dist/core/docs/capabilities.js +377 -0
- package/dist/core/docs/capabilities.js.map +1 -0
- package/dist/core/docs/corpus.js.map +1 -0
- package/dist/core/docs/demos.js +105 -0
- package/dist/core/docs/demos.js.map +1 -0
- package/dist/core/docs/get-page.js +182 -0
- package/dist/core/docs/get-page.js.map +1 -0
- package/dist/core/docs/hosts.js +74 -0
- package/dist/core/docs/hosts.js.map +1 -0
- package/dist/core/docs/search.js.map +1 -0
- package/dist/core/envelope.js +79 -0
- package/dist/core/envelope.js.map +1 -0
- package/dist/core/extract/imports.js +163 -0
- package/dist/core/extract/imports.js.map +1 -0
- package/dist/core/extract/input.js +140 -0
- package/dist/core/extract/input.js.map +1 -0
- package/dist/core/extract/line-map.js +90 -0
- package/dist/core/extract/line-map.js.map +1 -0
- package/dist/core/extract/ts-literal.js +963 -0
- package/dist/core/extract/ts-literal.js.map +1 -0
- package/dist/core/index.js +13 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/migrate/capabilities.js +143 -0
- package/dist/core/migrate/capabilities.js.map +1 -0
- package/dist/core/migrate/leftovers.js +119 -0
- package/dist/core/migrate/leftovers.js.map +1 -0
- package/dist/core/migrate/state.js +295 -0
- package/dist/core/migrate/state.js.map +1 -0
- package/dist/core/packages/cache.js +139 -0
- package/dist/core/packages/cache.js.map +1 -0
- package/dist/core/packages/capabilities.js +95 -0
- package/dist/core/packages/capabilities.js.map +1 -0
- package/dist/{adaptable/types.js → core/packages/entry-types.js} +1 -1
- package/dist/core/packages/entry-types.js.map +1 -0
- package/dist/core/packages/exec.js +37 -0
- package/dist/core/packages/exec.js.map +1 -0
- package/dist/core/packages/load-entry.js +123 -0
- package/dist/core/packages/load-entry.js.map +1 -0
- package/dist/core/packages/prepare.js +122 -0
- package/dist/core/packages/prepare.js.map +1 -0
- package/dist/core/packages/registry.js +75 -0
- package/dist/core/packages/registry.js.map +1 -0
- package/dist/core/packages/resolve.js +94 -0
- package/dist/core/packages/resolve.js.map +1 -0
- package/dist/core/packages/versions.js +71 -0
- package/dist/core/packages/versions.js.map +1 -0
- package/dist/core/ping.js +22 -0
- package/dist/core/ping.js.map +1 -0
- package/dist/core/register.js +18 -0
- package/dist/core/register.js.map +1 -0
- package/dist/core/scaffold/capabilities.js +434 -0
- package/dist/core/scaffold/capabilities.js.map +1 -0
- package/dist/core/scaffold/generate.js +409 -0
- package/dist/core/scaffold/generate.js.map +1 -0
- package/dist/core/scaffold/infer.js +441 -0
- package/dist/core/scaffold/infer.js.map +1 -0
- package/dist/core/scaffold/overlay.js +290 -0
- package/dist/core/scaffold/overlay.js.map +1 -0
- package/dist/core/scaffold/readers/csv.js +146 -0
- package/dist/core/scaffold/readers/csv.js.map +1 -0
- package/dist/core/scaffold/readers/dataset.js +53 -0
- package/dist/core/scaffold/readers/dataset.js.map +1 -0
- package/dist/core/scaffold/readers/index.js +62 -0
- package/dist/core/scaffold/readers/index.js.map +1 -0
- package/dist/core/scaffold/readers/json.js +101 -0
- package/dist/core/scaffold/readers/json.js.map +1 -0
- package/dist/core/scaffold/readers/xlsx.js +81 -0
- package/dist/core/scaffold/readers/xlsx.js.map +1 -0
- package/dist/core/scaffold/suggestions.js +57 -0
- package/dist/core/scaffold/suggestions.js.map +1 -0
- package/dist/core/scaffold/templates.js +59 -0
- package/dist/core/scaffold/templates.js.map +1 -0
- package/dist/core/setup/audit.js +91 -0
- package/dist/core/setup/audit.js.map +1 -0
- package/dist/core/setup/capabilities.js +80 -0
- package/dist/core/setup/capabilities.js.map +1 -0
- package/dist/core/setup/context.js +118 -0
- package/dist/core/setup/context.js.map +1 -0
- package/dist/core/setup/rules/cell-data-type.js +40 -0
- package/dist/core/setup/rules/cell-data-type.js.map +1 -0
- package/dist/core/setup/rules/coldef-ownership.js +67 -0
- package/dist/core/setup/rules/coldef-ownership.js.map +1 -0
- package/dist/core/setup/rules/css-import.js +34 -0
- package/dist/core/setup/rules/css-import.js.map +1 -0
- package/dist/core/setup/rules/direct-grid.js +42 -0
- package/dist/core/setup/rules/direct-grid.js.map +1 -0
- package/dist/core/setup/rules/index.js +53 -0
- package/dist/core/setup/rules/index.js.map +1 -0
- package/dist/core/setup/rules/licence-literal.js +23 -0
- package/dist/core/setup/rules/licence-literal.js.map +1 -0
- package/dist/core/setup/rules/modules.js +86 -0
- package/dist/core/setup/rules/modules.js.map +1 -0
- package/dist/core/setup/rules/react-stable-refs.js +20 -0
- package/dist/core/setup/rules/react-stable-refs.js.map +1 -0
- package/dist/core/setup/rules/ready-hook.js +33 -0
- package/dist/core/setup/rules/ready-hook.js.map +1 -0
- package/dist/core/setup/rules/required-options.js +43 -0
- package/dist/core/setup/rules/required-options.js.map +1 -0
- package/dist/core/setup/rules/rule.js +10 -0
- package/dist/core/setup/rules/rule.js.map +1 -0
- package/dist/core/setup/rules/version-skew.js +65 -0
- package/dist/core/setup/rules/version-skew.js.map +1 -0
- package/dist/core/state/capabilities.js +78 -0
- package/dist/core/state/capabilities.js.map +1 -0
- package/dist/core/state/check.js +216 -0
- package/dist/core/state/check.js.map +1 -0
- package/dist/core/state/columns.js +130 -0
- package/dist/core/state/columns.js.map +1 -0
- package/dist/core/state/did-you-mean.js +83 -0
- package/dist/core/state/did-you-mean.js.map +1 -0
- package/dist/core/state/expressions.js +170 -0
- package/dist/core/state/expressions.js.map +1 -0
- package/dist/core/state/known-names.js +96 -0
- package/dist/core/state/known-names.js.map +1 -0
- package/dist/core/state/rules/aql-legacy-shape.js +42 -0
- package/dist/core/state/rules/aql-legacy-shape.js.map +1 -0
- package/dist/core/state/rules/aql-missing.js +21 -0
- package/dist/core/state/rules/aql-missing.js.map +1 -0
- package/dist/core/state/rules/aql-syntax.js +77 -0
- package/dist/core/state/rules/aql-syntax.js.map +1 -0
- package/dist/core/state/rules/calculated-settings.js +23 -0
- package/dist/core/state/rules/calculated-settings.js.map +1 -0
- package/dist/core/state/rules/column-id-exists.js +29 -0
- package/dist/core/state/rules/column-id-exists.js.map +1 -0
- package/dist/core/state/rules/dashboard-module-names.js +31 -0
- package/dist/core/state/rules/dashboard-module-names.js.map +1 -0
- package/dist/core/state/rules/dashboard-toolbar-names.js +48 -0
- package/dist/core/state/rules/dashboard-toolbar-names.js.map +1 -0
- package/dist/core/state/rules/format-column-pivot-display-format.js +43 -0
- package/dist/core/state/rules/format-column-pivot-display-format.js.map +1 -0
- package/dist/core/state/rules/helpers.js +39 -0
- package/dist/core/state/rules/helpers.js.map +1 -0
- package/dist/core/state/rules/index.js +80 -0
- package/dist/core/state/rules/index.js.map +1 -0
- package/dist/core/state/rules/layout-column-refs.js +60 -0
- package/dist/core/state/rules/layout-column-refs.js.map +1 -0
- package/dist/core/state/rules/layout-current-exists.js +27 -0
- package/dist/core/state/rules/layout-current-exists.js.map +1 -0
- package/dist/core/state/rules/layout-non-empty.js +32 -0
- package/dist/core/state/rules/layout-non-empty.js.map +1 -0
- package/dist/core/state/rules/rule.js +2 -0
- package/dist/core/state/rules/rule.js.map +1 -0
- package/dist/core/state/rules/scope-column-refs.js +32 -0
- package/dist/core/state/rules/scope-column-refs.js.map +1 -0
- package/dist/core/state/rules/scope-shape.js +61 -0
- package/dist/core/state/rules/scope-shape.js.map +1 -0
- package/dist/core/state/rules/state-unique-name.js +40 -0
- package/dist/core/state/rules/state-unique-name.js.map +1 -0
- package/dist/core/state/rules/state-unknown-section.js +30 -0
- package/dist/core/state/rules/state-unknown-section.js.map +1 -0
- package/dist/core/state/rules/type-discriminator.js +70 -0
- package/dist/core/state/rules/type-discriminator.js.map +1 -0
- package/dist/core/state/type-check.js +408 -0
- package/dist/core/state/type-check.js.map +1 -0
- package/dist/mcp/index.js +109 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/server.js +110 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/skills/adaptable-debug/SKILL.md +145 -0
- package/dist/skills/adaptable-debug/VERSION.md +1 -0
- package/dist/skills/adaptable-debug/references/_shared/agents-snippet.md +3 -0
- package/dist/skills/adaptable-debug/references/_shared/capabilities.md +35 -0
- package/dist/skills/adaptable-debug/references/_shared/conversation.md +20 -0
- package/dist/skills/adaptable-debug/references/_shared/docs-protocol.md +36 -0
- package/dist/skills/adaptable-debug/references/_shared/grounding.md +54 -0
- package/dist/skills/adaptable-debug/references/_shared/guards.md +20 -0
- package/dist/skills/adaptable-debug/references/_shared/validation-loop.md +42 -0
- package/dist/skills/adaptable-debug/references/devtools-api.md +36 -0
- package/dist/skills/adaptable-debug/references/feedback-template.md +36 -0
- package/dist/skills/adaptable-debug/references/symptoms.md +37 -0
- package/dist/skills/adaptable-debug/references/ticket-template.md +50 -0
- package/dist/skills/adaptable-dev/SKILL.md +97 -0
- package/dist/skills/adaptable-dev/VERSION.md +1 -0
- package/dist/skills/adaptable-dev/references/_shared/agents-snippet.md +3 -0
- package/dist/skills/adaptable-dev/references/_shared/capabilities.md +35 -0
- package/dist/skills/adaptable-dev/references/_shared/conversation.md +20 -0
- package/dist/skills/adaptable-dev/references/_shared/docs-protocol.md +36 -0
- package/dist/skills/adaptable-dev/references/_shared/grounding.md +54 -0
- package/dist/skills/adaptable-dev/references/_shared/guards.md +20 -0
- package/dist/skills/adaptable-dev/references/_shared/validation-loop.md +42 -0
- package/dist/skills/adaptable-dev/references/recommendations.md +93 -0
- package/dist/skills/adaptable-setup/SKILL.md +158 -0
- package/dist/skills/adaptable-setup/VERSION.md +1 -0
- package/dist/skills/adaptable-setup/references/_shared/agents-snippet.md +3 -0
- package/dist/skills/adaptable-setup/references/_shared/capabilities.md +35 -0
- package/dist/skills/adaptable-setup/references/_shared/conversation.md +20 -0
- package/dist/skills/adaptable-setup/references/_shared/docs-protocol.md +36 -0
- package/dist/skills/adaptable-setup/references/_shared/grounding.md +54 -0
- package/dist/skills/adaptable-setup/references/_shared/guards.md +20 -0
- package/dist/skills/adaptable-setup/references/_shared/validation-loop.md +42 -0
- package/dist/skills/adaptable-setup/references/paths.md +45 -0
- package/dist/skills/adaptable-upgrade/SKILL.md +269 -0
- package/dist/skills/adaptable-upgrade/VERSION.md +1 -0
- package/dist/skills/adaptable-upgrade/references/_shared/agents-snippet.md +3 -0
- package/dist/skills/adaptable-upgrade/references/_shared/capabilities.md +35 -0
- package/dist/skills/adaptable-upgrade/references/_shared/conversation.md +20 -0
- package/dist/skills/adaptable-upgrade/references/_shared/docs-protocol.md +36 -0
- package/dist/skills/adaptable-upgrade/references/_shared/grounding.md +54 -0
- package/dist/skills/adaptable-upgrade/references/_shared/guards.md +20 -0
- package/dist/skills/adaptable-upgrade/references/_shared/validation-loop.md +42 -0
- package/dist/skills/adaptable-upgrade/references/apply-order.md +171 -0
- package/dist/skills/adaptable-upgrade/references/plan-preamble.md +151 -0
- package/dist/skills/adaptable-upgrade/scripts/adaptable.mjs +24 -0
- package/package.json +34 -13
- package/dist/adaptable/loadEntry.js +0 -94
- package/dist/adaptable/loadEntry.js.map +0 -1
- package/dist/adaptable/types.js.map +0 -1
- package/dist/cache.js +0 -11
- package/dist/cache.js.map +0 -1
- package/dist/checks/stateRules.js +0 -389
- package/dist/checks/stateRules.js.map +0 -1
- package/dist/checks/stateTypes.js +0 -142
- package/dist/checks/stateTypes.js.map +0 -1
- package/dist/context.js +0 -39
- package/dist/context.js.map +0 -1
- package/dist/docs/corpus.js.map +0 -1
- package/dist/docs/search.js.map +0 -1
- package/dist/docs/source.js +0 -151
- package/dist/docs/source.js.map +0 -1
- package/dist/environment.js +0 -181
- package/dist/environment.js.map +0 -1
- package/dist/index.js +0 -7
- package/dist/index.js.map +0 -1
- package/dist/prompts.js +0 -84
- package/dist/prompts.js.map +0 -1
- package/dist/resources.js +0 -59
- package/dist/resources.js.map +0 -1
- package/dist/server.js +0 -23
- package/dist/server.js.map +0 -1
- package/dist/tools/checks.js +0 -250
- package/dist/tools/checks.js.map +0 -1
- package/dist/tools/docs.js +0 -173
- package/dist/tools/docs.js.map +0 -1
- package/dist/tools/environment.js +0 -52
- package/dist/tools/environment.js.map +0 -1
- package/skills/adaptable-dev/SKILL.md +0 -42
- /package/dist/{docs → core/docs}/corpus.js +0 -0
- /package/dist/{docs → core/docs}/search.js +0 -0
|
@@ -0,0 +1,963 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Locates the State / Options / `columnDefs` literal in a source file and evaluates
|
|
3
|
+
* it to a JSON value with a line map (plan §8.4 Input, §8.6 `options-shape`, §8.7).
|
|
4
|
+
*
|
|
5
|
+
* TypeScript compiler API, `createSourceFile` only — no program, no type-check.
|
|
6
|
+
* Whatever cannot be evaluated statically (functions, unresolvable spreads, calls,
|
|
7
|
+
* template literals with substitutions, computed keys, member accesses that do not
|
|
8
|
+
* land on a local literal or enum, identifiers imported from packages) is stubbed
|
|
9
|
+
* as `null` in the value and listed under `unverifiable` with its path and reason;
|
|
10
|
+
* it is never an error (plan §21, "every such path is listed").
|
|
11
|
+
*/
|
|
12
|
+
import { basename, extname } from 'node:path';
|
|
13
|
+
import ts from 'typescript';
|
|
14
|
+
import { LineIndex, ROOT_PATH, childPath, indexPath, mergeShiftedIndices, } from './line-map.js';
|
|
15
|
+
export class SyntaxErrorInSource extends Error {
|
|
16
|
+
file;
|
|
17
|
+
line;
|
|
18
|
+
column;
|
|
19
|
+
detail;
|
|
20
|
+
constructor(file, line, column, detail) {
|
|
21
|
+
super(`${file}:${line}:${column}: ${detail}`);
|
|
22
|
+
this.file = file;
|
|
23
|
+
this.line = line;
|
|
24
|
+
this.column = column;
|
|
25
|
+
this.detail = detail;
|
|
26
|
+
this.name = 'SyntaxErrorInSource';
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export const SOURCE_EXTENSIONS = ['.ts', '.tsx', '.mts', '.cts', '.js', '.jsx', '.mjs', '.cjs', '.vue'];
|
|
30
|
+
export function isSourceFile(file) {
|
|
31
|
+
return SOURCE_EXTENSIONS.includes(extname(file).toLowerCase());
|
|
32
|
+
}
|
|
33
|
+
function scriptKindFor(file) {
|
|
34
|
+
switch (extname(file).toLowerCase()) {
|
|
35
|
+
case '.tsx':
|
|
36
|
+
return ts.ScriptKind.TSX;
|
|
37
|
+
case '.js':
|
|
38
|
+
case '.mjs':
|
|
39
|
+
case '.cjs':
|
|
40
|
+
return ts.ScriptKind.JS;
|
|
41
|
+
case '.jsx':
|
|
42
|
+
return ts.ScriptKind.JSX;
|
|
43
|
+
default:
|
|
44
|
+
return ts.ScriptKind.TS;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const SCRIPT_BLOCK = /<script\b([^>]*)>([\s\S]*?)<\/script>/gi;
|
|
48
|
+
/**
|
|
49
|
+
* A `.vue` SFC as parseable text: everything outside the `<script>` / `<script setup>`
|
|
50
|
+
* blocks is blanked (newlines kept), so every node offset is an offset into the
|
|
51
|
+
* original file and lines map back without arithmetic.
|
|
52
|
+
*/
|
|
53
|
+
function blankVueTemplate(text) {
|
|
54
|
+
let script = text.replace(/[^\n]/g, ' ');
|
|
55
|
+
let kind = ts.ScriptKind.JS;
|
|
56
|
+
for (const match of text.matchAll(SCRIPT_BLOCK)) {
|
|
57
|
+
const attrs = match[1];
|
|
58
|
+
const body = match[2];
|
|
59
|
+
// match[0] = `<script…>` + body + `</script>`
|
|
60
|
+
const start = match.index + match[0].length - '</script>'.length - body.length;
|
|
61
|
+
script = script.slice(0, start) + body + script.slice(start + body.length);
|
|
62
|
+
const lang = /\blang\s*=\s*["']?(\w+)/.exec(attrs)?.[1]?.toLowerCase();
|
|
63
|
+
if (lang === 'tsx') {
|
|
64
|
+
kind = ts.ScriptKind.TSX;
|
|
65
|
+
}
|
|
66
|
+
else if (lang === 'ts' && kind !== ts.ScriptKind.TSX) {
|
|
67
|
+
kind = ts.ScriptKind.TS;
|
|
68
|
+
}
|
|
69
|
+
else if (lang === 'jsx' && kind === ts.ScriptKind.JS) {
|
|
70
|
+
kind = ts.ScriptKind.JSX;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return { script, kind };
|
|
74
|
+
}
|
|
75
|
+
function parseDiagnostics(sf) {
|
|
76
|
+
// `parseDiagnostics` is the parser's own list; it is not in the public type but has
|
|
77
|
+
// been on SourceFile since TS 2.x and is the only way to see syntax errors without a program.
|
|
78
|
+
return sf.parseDiagnostics ?? [];
|
|
79
|
+
}
|
|
80
|
+
/** Parses a source file (or a JSON document as a JS expression when `asExpression`); throws `SyntaxErrorInSource`. */
|
|
81
|
+
export function parseSource(text, file, options = {}) {
|
|
82
|
+
const clean = text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;
|
|
83
|
+
let scriptText = clean;
|
|
84
|
+
let kind = scriptKindFor(file);
|
|
85
|
+
let delta = 0;
|
|
86
|
+
if (options.asExpression) {
|
|
87
|
+
scriptText = `(${clean})`;
|
|
88
|
+
kind = ts.ScriptKind.JS;
|
|
89
|
+
delta = 1;
|
|
90
|
+
}
|
|
91
|
+
else if (extname(file).toLowerCase() === '.vue') {
|
|
92
|
+
({ script: scriptText, kind } = blankVueTemplate(clean));
|
|
93
|
+
}
|
|
94
|
+
const sf = ts.createSourceFile(file, scriptText, ts.ScriptTarget.Latest, true, kind);
|
|
95
|
+
const index = new LineIndex(clean);
|
|
96
|
+
const parsed = { file, text: clean, sf, delta, index, declarations: indexDeclarations(sf) };
|
|
97
|
+
const [first] = parseDiagnostics(sf);
|
|
98
|
+
if (first) {
|
|
99
|
+
const { line, column } = index.positionAt(Math.max(0, first.start - delta));
|
|
100
|
+
throw new SyntaxErrorInSource(file, line, column, ts.flattenDiagnosticMessageText(first.messageText, ' '));
|
|
101
|
+
}
|
|
102
|
+
return parsed;
|
|
103
|
+
}
|
|
104
|
+
function push(map, key, value) {
|
|
105
|
+
const list = map.get(key);
|
|
106
|
+
if (list) {
|
|
107
|
+
list.push(value);
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
map.set(key, [value]);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function indexDeclarations(sf) {
|
|
114
|
+
const index = {
|
|
115
|
+
variables: new Map(),
|
|
116
|
+
classProperties: new Map(),
|
|
117
|
+
enums: new Map(),
|
|
118
|
+
functions: new Map(),
|
|
119
|
+
imports: new Map(),
|
|
120
|
+
};
|
|
121
|
+
const visit = (node) => {
|
|
122
|
+
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name)) {
|
|
123
|
+
push(index.variables, node.name.text, node);
|
|
124
|
+
}
|
|
125
|
+
else if (ts.isPropertyDeclaration(node) && (ts.isIdentifier(node.name) || ts.isStringLiteral(node.name))) {
|
|
126
|
+
push(index.classProperties, node.name.text, node);
|
|
127
|
+
}
|
|
128
|
+
else if (ts.isEnumDeclaration(node)) {
|
|
129
|
+
index.enums.set(node.name.text, node);
|
|
130
|
+
}
|
|
131
|
+
else if (ts.isFunctionDeclaration(node) && node.name) {
|
|
132
|
+
index.functions.set(node.name.text, node);
|
|
133
|
+
}
|
|
134
|
+
else if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) {
|
|
135
|
+
const specifier = node.moduleSpecifier.text;
|
|
136
|
+
const clause = node.importClause;
|
|
137
|
+
if (clause?.name) {
|
|
138
|
+
index.imports.set(clause.name.text, { kind: 'default', local: clause.name.text, imported: 'default', specifier, node, localNode: clause.name });
|
|
139
|
+
}
|
|
140
|
+
if (clause?.namedBindings) {
|
|
141
|
+
if (ts.isNamespaceImport(clause.namedBindings)) {
|
|
142
|
+
const local = clause.namedBindings.name.text;
|
|
143
|
+
index.imports.set(local, { kind: 'namespace', local, imported: '*', specifier, node, localNode: clause.namedBindings.name });
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
for (const element of clause.namedBindings.elements) {
|
|
147
|
+
const local = element.name.text;
|
|
148
|
+
const imported = element.propertyName?.text ?? local;
|
|
149
|
+
index.imports.set(local, { kind: 'named', local, imported, specifier, node, localNode: element.name });
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
ts.forEachChild(node, visit);
|
|
155
|
+
};
|
|
156
|
+
visit(sf);
|
|
157
|
+
return index;
|
|
158
|
+
}
|
|
159
|
+
function scopeOf(node) {
|
|
160
|
+
let current = node;
|
|
161
|
+
while (!ts.isSourceFile(current) && !ts.isBlock(current) && !ts.isModuleBlock(current) && !ts.isCaseBlock(current)) {
|
|
162
|
+
current = current.parent;
|
|
163
|
+
}
|
|
164
|
+
return current;
|
|
165
|
+
}
|
|
166
|
+
/** The declaration of `name` whose scope encloses `pos` most tightly; a module-level one otherwise */
|
|
167
|
+
function pickDeclaration(candidates, pos) {
|
|
168
|
+
if (!candidates?.length) {
|
|
169
|
+
return undefined;
|
|
170
|
+
}
|
|
171
|
+
let best;
|
|
172
|
+
if (pos !== undefined) {
|
|
173
|
+
for (const node of candidates) {
|
|
174
|
+
const scope = scopeOf(node);
|
|
175
|
+
if (scope.pos <= pos && pos < scope.end) {
|
|
176
|
+
const size = scope.end - scope.pos;
|
|
177
|
+
if (!best || size < best.size) {
|
|
178
|
+
best = { node, size };
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return best?.node ?? candidates.find((node) => ts.isSourceFile(scopeOf(node))) ?? candidates[0];
|
|
184
|
+
}
|
|
185
|
+
export function createContext(parsed, options = {}) {
|
|
186
|
+
return {
|
|
187
|
+
parsed,
|
|
188
|
+
imports: options.imports,
|
|
189
|
+
sink: options.sink ?? { lineMap: {}, unverifiable: [] },
|
|
190
|
+
stack: options.stack ?? new Set(),
|
|
191
|
+
hop: options.hop ?? 0,
|
|
192
|
+
...(options.chain ? { chain: options.chain } : {}),
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
const OMIT = Symbol('omit');
|
|
196
|
+
export function positionOf(ctx, node) {
|
|
197
|
+
const start = node.getStart(ctx.parsed.sf) - ctx.parsed.delta;
|
|
198
|
+
return { file: ctx.parsed.file, ...ctx.parsed.index.positionAt(Math.max(0, start)) };
|
|
199
|
+
}
|
|
200
|
+
function recordValue(ctx, path, node) {
|
|
201
|
+
const entry = ctx.sink.lineMap[path];
|
|
202
|
+
const value = positionOf(ctx, node);
|
|
203
|
+
ctx.sink.lineMap[path] = entry ? { ...entry, value } : { value };
|
|
204
|
+
}
|
|
205
|
+
function recordKey(ctx, path, node) {
|
|
206
|
+
const entry = ctx.sink.lineMap[path];
|
|
207
|
+
const key = positionOf(ctx, node);
|
|
208
|
+
ctx.sink.lineMap[path] = entry ? { ...entry, key } : { value: key, key };
|
|
209
|
+
}
|
|
210
|
+
function report(ctx, path, node, kind, reason) {
|
|
211
|
+
recordValue(ctx, path, node);
|
|
212
|
+
ctx.sink.unverifiable.push({ path, kind, reason, ...positionOf(ctx, node) });
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
export function snippet(node, max = 60) {
|
|
216
|
+
const text = node.getText(node.getSourceFile()).replace(/\s+/g, ' ');
|
|
217
|
+
return text.length > max ? `${text.slice(0, max - 1)}…` : text;
|
|
218
|
+
}
|
|
219
|
+
function describe(node) {
|
|
220
|
+
switch (node.kind) {
|
|
221
|
+
case ts.SyntaxKind.ObjectLiteralExpression:
|
|
222
|
+
return 'an object literal';
|
|
223
|
+
case ts.SyntaxKind.ArrayLiteralExpression:
|
|
224
|
+
return 'an array literal';
|
|
225
|
+
case ts.SyntaxKind.CallExpression:
|
|
226
|
+
case ts.SyntaxKind.NewExpression:
|
|
227
|
+
case ts.SyntaxKind.TaggedTemplateExpression:
|
|
228
|
+
return 'a call';
|
|
229
|
+
case ts.SyntaxKind.ArrowFunction:
|
|
230
|
+
case ts.SyntaxKind.FunctionExpression:
|
|
231
|
+
case ts.SyntaxKind.FunctionDeclaration:
|
|
232
|
+
case ts.SyntaxKind.MethodDeclaration:
|
|
233
|
+
return 'a function';
|
|
234
|
+
case ts.SyntaxKind.EnumDeclaration:
|
|
235
|
+
return 'an enum';
|
|
236
|
+
case ts.SyntaxKind.ClassDeclaration:
|
|
237
|
+
case ts.SyntaxKind.ClassExpression:
|
|
238
|
+
return 'a class';
|
|
239
|
+
case ts.SyntaxKind.TemplateExpression:
|
|
240
|
+
return 'a template literal with substitutions';
|
|
241
|
+
default:
|
|
242
|
+
return `a ${ts.SyntaxKind[node.kind].replace(/([a-z])([A-Z])/g, '$1 $2').toLowerCase()}`;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/** Strips parentheses and type-only wrappers */
|
|
246
|
+
export function unwrap(node) {
|
|
247
|
+
let current = node;
|
|
248
|
+
for (;;) {
|
|
249
|
+
if (ts.isParenthesizedExpression(current) ||
|
|
250
|
+
ts.isAsExpression(current) ||
|
|
251
|
+
ts.isSatisfiesExpression(current) ||
|
|
252
|
+
ts.isNonNullExpression(current) ||
|
|
253
|
+
ts.isTypeAssertionExpression(current)) {
|
|
254
|
+
current = current.expression;
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
return current;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
function isReferenceLike(node) {
|
|
262
|
+
return (ts.isIdentifier(node) ||
|
|
263
|
+
ts.isPropertyAccessExpression(node) ||
|
|
264
|
+
(ts.isElementAccessExpression(node) && literalKey(node.argumentExpression) !== undefined) ||
|
|
265
|
+
node.kind === ts.SyntaxKind.ThisKeyword);
|
|
266
|
+
}
|
|
267
|
+
function literalKey(node) {
|
|
268
|
+
const inner = unwrap(node);
|
|
269
|
+
if (ts.isStringLiteral(inner) || ts.isNoSubstitutionTemplateLiteral(inner) || ts.isNumericLiteral(inner)) {
|
|
270
|
+
return inner.text;
|
|
271
|
+
}
|
|
272
|
+
return undefined;
|
|
273
|
+
}
|
|
274
|
+
const GLOBAL_NUMBERS = { NaN: Number.NaN, Infinity: Number.POSITIVE_INFINITY };
|
|
275
|
+
function isRelativeSpecifier(specifier) {
|
|
276
|
+
return specifier.startsWith('./') || specifier.startsWith('../') || specifier === '.' || specifier === '..';
|
|
277
|
+
}
|
|
278
|
+
/** One resolution step for an identifier / member access; the chain is followed by `resolveReference` */
|
|
279
|
+
function resolveOnce(node, ctx) {
|
|
280
|
+
const file = basename(ctx.parsed.file);
|
|
281
|
+
const { declarations } = ctx.parsed;
|
|
282
|
+
if (ts.isIdentifier(node)) {
|
|
283
|
+
const name = node.text;
|
|
284
|
+
const variable = pickDeclaration(declarations.variables.get(name), node.getStart(ctx.parsed.sf));
|
|
285
|
+
if (variable) {
|
|
286
|
+
if (!variable.initializer) {
|
|
287
|
+
return { ok: false, kind: 'unresolved-identifier', reason: `\`${name}\` is declared without an initializer in ${file}` };
|
|
288
|
+
}
|
|
289
|
+
return { ok: true, node: variable.initializer, ctx, decls: [], decl: variable };
|
|
290
|
+
}
|
|
291
|
+
const fn = declarations.functions.get(name);
|
|
292
|
+
if (fn) {
|
|
293
|
+
return { ok: true, node: fn, ctx, decls: [], decl: fn };
|
|
294
|
+
}
|
|
295
|
+
const enumDecl = declarations.enums.get(name);
|
|
296
|
+
if (enumDecl) {
|
|
297
|
+
return { ok: true, node: enumDecl, ctx, decls: [], decl: enumDecl };
|
|
298
|
+
}
|
|
299
|
+
const binding = declarations.imports.get(name);
|
|
300
|
+
if (binding) {
|
|
301
|
+
return resolveImport(binding, undefined, ctx);
|
|
302
|
+
}
|
|
303
|
+
return { ok: false, kind: 'unresolved-identifier', reason: `\`${name}\` has no visible literal initializer in ${file}` };
|
|
304
|
+
}
|
|
305
|
+
// this.x · a.b · a['b']
|
|
306
|
+
let base;
|
|
307
|
+
let member;
|
|
308
|
+
if (ts.isPropertyAccessExpression(node)) {
|
|
309
|
+
base = unwrap(node.expression);
|
|
310
|
+
member = node.name.text;
|
|
311
|
+
}
|
|
312
|
+
else if (ts.isElementAccessExpression(node)) {
|
|
313
|
+
base = unwrap(node.expression);
|
|
314
|
+
member = literalKey(node.argumentExpression);
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
return { ok: false, kind: 'expression', reason: `\`${snippet(node)}\` is ${describe(node)}` };
|
|
318
|
+
}
|
|
319
|
+
const text = snippet(node);
|
|
320
|
+
if (base.kind === ts.SyntaxKind.ThisKeyword) {
|
|
321
|
+
const property = pickDeclaration(declarations.classProperties.get(member), node.getStart(ctx.parsed.sf));
|
|
322
|
+
if (!property) {
|
|
323
|
+
return { ok: false, kind: 'member-access', reason: `\`${text}\`: no class property \`${member}\` with an initializer in ${file}` };
|
|
324
|
+
}
|
|
325
|
+
if (!property.initializer) {
|
|
326
|
+
return { ok: false, kind: 'member-access', reason: `\`${text}\`: class property \`${member}\` has no initializer` };
|
|
327
|
+
}
|
|
328
|
+
return { ok: true, node: property.initializer, ctx, decls: [], decl: property };
|
|
329
|
+
}
|
|
330
|
+
if (ts.isIdentifier(base)) {
|
|
331
|
+
const binding = declarations.imports.get(base.text);
|
|
332
|
+
if (binding?.kind === 'namespace' && !declarations.variables.has(base.text)) {
|
|
333
|
+
return resolveImport(binding, member, ctx);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
const baseRef = resolveReference(base, ctx);
|
|
337
|
+
if (!baseRef.ok) {
|
|
338
|
+
const kind = baseRef.kind === 'non-relative-import' || baseRef.kind === 'unresolved-import' ? baseRef.kind : 'member-access';
|
|
339
|
+
// a nested member access already carries its own `a.b`: prefix; keep the innermost cause once
|
|
340
|
+
const cause = baseRef.kind === 'member-access' ? baseRef.reason.replace(/^`[^`]*`: /, '') : baseRef.reason;
|
|
341
|
+
return { ok: false, kind, reason: `\`${text}\`: ${cause}` };
|
|
342
|
+
}
|
|
343
|
+
const target = unwrap(baseRef.node);
|
|
344
|
+
if (ts.isEnumDeclaration(target)) {
|
|
345
|
+
const value = enumMemberValue(target, member);
|
|
346
|
+
if (value === undefined) {
|
|
347
|
+
return { ok: false, kind: 'member-access', reason: `\`${text}\`: enum member \`${member}\` has no literal value` };
|
|
348
|
+
}
|
|
349
|
+
const memberNode = target.members.find((m) => propertyName(m.name) === member) ?? target;
|
|
350
|
+
return { ok: true, node: memberNode, ctx: baseRef.ctx, decls: baseRef.decls, constant: value };
|
|
351
|
+
}
|
|
352
|
+
if (ts.isObjectLiteralExpression(target)) {
|
|
353
|
+
const property = target.properties.find((p) => p.name && propertyName(p.name) === member);
|
|
354
|
+
if (!property) {
|
|
355
|
+
return { ok: false, kind: 'member-access', reason: `\`${text}\`: the literal has no property \`${member}\`` };
|
|
356
|
+
}
|
|
357
|
+
if (ts.isPropertyAssignment(property)) {
|
|
358
|
+
return { ok: true, node: property.initializer, ctx: baseRef.ctx, decls: baseRef.decls };
|
|
359
|
+
}
|
|
360
|
+
if (ts.isShorthandPropertyAssignment(property)) {
|
|
361
|
+
return { ok: true, node: property.name, ctx: baseRef.ctx, decls: baseRef.decls };
|
|
362
|
+
}
|
|
363
|
+
return { ok: true, node: property, ctx: baseRef.ctx, decls: baseRef.decls };
|
|
364
|
+
}
|
|
365
|
+
if (ts.isArrayLiteralExpression(target) && /^\d+$/.test(member)) {
|
|
366
|
+
const element = target.elements[Number(member)];
|
|
367
|
+
if (element && !ts.isSpreadElement(element) && !ts.isOmittedExpression(element)) {
|
|
368
|
+
return { ok: true, node: element, ctx: baseRef.ctx, decls: baseRef.decls };
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
return { ok: false, kind: 'member-access', reason: `\`${text}\`: \`${snippet(base)}\` is ${describe(target)}` };
|
|
372
|
+
}
|
|
373
|
+
function resolveImport(binding, member, ctx) {
|
|
374
|
+
const what = member ? `\`${binding.local}.${member}\`` : `\`${binding.local}\``;
|
|
375
|
+
if (!isRelativeSpecifier(binding.specifier)) {
|
|
376
|
+
return {
|
|
377
|
+
ok: false,
|
|
378
|
+
kind: 'non-relative-import',
|
|
379
|
+
reason: `${what} is imported from '${binding.specifier}', a package rather than a relative file`,
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
if (!ctx.imports) {
|
|
383
|
+
return {
|
|
384
|
+
ok: false,
|
|
385
|
+
kind: 'unresolved-import',
|
|
386
|
+
reason: `${what} is imported from '${binding.specifier}' in ${basename(ctx.parsed.file)}, a second hop; the extractor follows one hop of relative imports`,
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
return ctx.imports.resolve(binding, member, ctx);
|
|
390
|
+
}
|
|
391
|
+
function propertyName(name) {
|
|
392
|
+
if (ts.isIdentifier(name) || ts.isStringLiteral(name) || ts.isNumericLiteral(name) || ts.isNoSubstitutionTemplateLiteral(name)) {
|
|
393
|
+
return name.text;
|
|
394
|
+
}
|
|
395
|
+
if (ts.isPrivateIdentifier(name)) {
|
|
396
|
+
return name.text;
|
|
397
|
+
}
|
|
398
|
+
if (ts.isComputedPropertyName(name)) {
|
|
399
|
+
return literalKey(name.expression);
|
|
400
|
+
}
|
|
401
|
+
return undefined;
|
|
402
|
+
}
|
|
403
|
+
function enumMemberValue(decl, member) {
|
|
404
|
+
let next = 0;
|
|
405
|
+
for (const m of decl.members) {
|
|
406
|
+
let value;
|
|
407
|
+
if (m.initializer) {
|
|
408
|
+
const init = unwrap(m.initializer);
|
|
409
|
+
if (ts.isStringLiteral(init) || ts.isNoSubstitutionTemplateLiteral(init)) {
|
|
410
|
+
value = init.text;
|
|
411
|
+
next = undefined;
|
|
412
|
+
}
|
|
413
|
+
else if (ts.isNumericLiteral(init)) {
|
|
414
|
+
value = Number(init.text);
|
|
415
|
+
next = value + 1;
|
|
416
|
+
}
|
|
417
|
+
else if (ts.isPrefixUnaryExpression(init) && init.operator === ts.SyntaxKind.MinusToken && ts.isNumericLiteral(init.operand)) {
|
|
418
|
+
value = -Number(init.operand.text);
|
|
419
|
+
next = value + 1;
|
|
420
|
+
}
|
|
421
|
+
else {
|
|
422
|
+
next = undefined;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
else if (next !== undefined) {
|
|
426
|
+
value = next;
|
|
427
|
+
next += 1;
|
|
428
|
+
}
|
|
429
|
+
if (propertyName(m.name) === member) {
|
|
430
|
+
return value;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
return undefined;
|
|
434
|
+
}
|
|
435
|
+
/** Follows identifier / member chains to the node that is not itself a reference */
|
|
436
|
+
export function resolveReference(expr, ctx) {
|
|
437
|
+
let node = unwrap(expr);
|
|
438
|
+
let current = ctx;
|
|
439
|
+
const decls = [];
|
|
440
|
+
while (isReferenceLike(node)) {
|
|
441
|
+
const step = resolveOnce(node, current);
|
|
442
|
+
if (!step.ok) {
|
|
443
|
+
return step;
|
|
444
|
+
}
|
|
445
|
+
if (step.decl) {
|
|
446
|
+
if (current.stack.has(step.decl) || decls.includes(step.decl)) {
|
|
447
|
+
return { ok: false, kind: 'unresolved-identifier', reason: `\`${snippet(node)}\` refers to itself (circular reference)` };
|
|
448
|
+
}
|
|
449
|
+
decls.push(step.decl);
|
|
450
|
+
}
|
|
451
|
+
decls.push(...step.decls);
|
|
452
|
+
if (step.constant !== undefined) {
|
|
453
|
+
return { ok: true, node: step.node, ctx: step.ctx, decls, constant: step.constant };
|
|
454
|
+
}
|
|
455
|
+
node = unwrap(step.node);
|
|
456
|
+
current = step.ctx;
|
|
457
|
+
}
|
|
458
|
+
return { ok: true, node, ctx: current, decls };
|
|
459
|
+
}
|
|
460
|
+
function withDecls(ctx, decls, fn) {
|
|
461
|
+
for (const decl of decls) {
|
|
462
|
+
ctx.stack.add(decl);
|
|
463
|
+
}
|
|
464
|
+
try {
|
|
465
|
+
return fn();
|
|
466
|
+
}
|
|
467
|
+
finally {
|
|
468
|
+
for (const decl of decls) {
|
|
469
|
+
ctx.stack.delete(decl);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
/** Evaluates a node into a JSON value, recording positions and unverifiable paths in `ctx.sink` */
|
|
474
|
+
export function evaluate(expr, path, ctx) {
|
|
475
|
+
const result = evaluateNode(expr, path, ctx);
|
|
476
|
+
return result === OMIT ? null : result;
|
|
477
|
+
}
|
|
478
|
+
function evaluateNode(expr, path, ctx) {
|
|
479
|
+
const node = unwrap(expr);
|
|
480
|
+
if (ts.isIdentifier(node) && node.text === 'undefined') {
|
|
481
|
+
return OMIT;
|
|
482
|
+
}
|
|
483
|
+
if (ts.isIdentifier(node) && node.text in GLOBAL_NUMBERS && !ctx.parsed.declarations.variables.has(node.text)) {
|
|
484
|
+
recordValue(ctx, path, node);
|
|
485
|
+
return GLOBAL_NUMBERS[node.text];
|
|
486
|
+
}
|
|
487
|
+
if (isReferenceLike(node)) {
|
|
488
|
+
const ref = resolveReference(node, ctx);
|
|
489
|
+
if (!ref.ok) {
|
|
490
|
+
return report(ctx, path, node, ref.kind, ref.reason);
|
|
491
|
+
}
|
|
492
|
+
if (ref.constant !== undefined) {
|
|
493
|
+
recordValue(ref.ctx, path, ref.node);
|
|
494
|
+
return ref.constant;
|
|
495
|
+
}
|
|
496
|
+
return withDecls(ctx, ref.decls, () => evaluateNode(ref.node, path, ref.ctx));
|
|
497
|
+
}
|
|
498
|
+
if (ts.isObjectLiteralExpression(node)) {
|
|
499
|
+
return evaluateObject(node, path, ctx);
|
|
500
|
+
}
|
|
501
|
+
if (ts.isArrayLiteralExpression(node)) {
|
|
502
|
+
return evaluateArray(node, path, ctx);
|
|
503
|
+
}
|
|
504
|
+
if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) {
|
|
505
|
+
recordValue(ctx, path, node);
|
|
506
|
+
return node.text;
|
|
507
|
+
}
|
|
508
|
+
if (ts.isNumericLiteral(node)) {
|
|
509
|
+
recordValue(ctx, path, node);
|
|
510
|
+
return Number(node.text);
|
|
511
|
+
}
|
|
512
|
+
if (ts.isPrefixUnaryExpression(node) && (node.operator === ts.SyntaxKind.MinusToken || node.operator === ts.SyntaxKind.PlusToken)) {
|
|
513
|
+
const operand = unwrap(node.operand);
|
|
514
|
+
const sign = node.operator === ts.SyntaxKind.MinusToken ? -1 : 1;
|
|
515
|
+
if (ts.isNumericLiteral(operand)) {
|
|
516
|
+
recordValue(ctx, path, node);
|
|
517
|
+
return sign * Number(operand.text);
|
|
518
|
+
}
|
|
519
|
+
if (ts.isIdentifier(operand) && operand.text in GLOBAL_NUMBERS) {
|
|
520
|
+
recordValue(ctx, path, node);
|
|
521
|
+
return sign * GLOBAL_NUMBERS[operand.text];
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
switch (node.kind) {
|
|
525
|
+
case ts.SyntaxKind.TrueKeyword:
|
|
526
|
+
recordValue(ctx, path, node);
|
|
527
|
+
return true;
|
|
528
|
+
case ts.SyntaxKind.FalseKeyword:
|
|
529
|
+
recordValue(ctx, path, node);
|
|
530
|
+
return false;
|
|
531
|
+
case ts.SyntaxKind.NullKeyword:
|
|
532
|
+
recordValue(ctx, path, node);
|
|
533
|
+
return null;
|
|
534
|
+
case ts.SyntaxKind.TemplateExpression:
|
|
535
|
+
return report(ctx, path, node, 'template', `\`${snippet(node)}\` is a template literal with substitutions`);
|
|
536
|
+
case ts.SyntaxKind.ArrowFunction:
|
|
537
|
+
case ts.SyntaxKind.FunctionExpression:
|
|
538
|
+
case ts.SyntaxKind.FunctionDeclaration:
|
|
539
|
+
case ts.SyntaxKind.MethodDeclaration:
|
|
540
|
+
case ts.SyntaxKind.GetAccessor:
|
|
541
|
+
case ts.SyntaxKind.SetAccessor:
|
|
542
|
+
return report(ctx, path, node, 'function', `\`${snippet(node, 40)}\` is a function`);
|
|
543
|
+
case ts.SyntaxKind.CallExpression:
|
|
544
|
+
case ts.SyntaxKind.NewExpression:
|
|
545
|
+
case ts.SyntaxKind.TaggedTemplateExpression:
|
|
546
|
+
return report(ctx, path, node, 'call', `\`${snippet(node)}\` is a call; its result is not visible statically`);
|
|
547
|
+
default:
|
|
548
|
+
return report(ctx, path, node, 'expression', `\`${snippet(node)}\` is ${describe(node)}`);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
function evaluateObject(node, path, ctx) {
|
|
552
|
+
const out = {};
|
|
553
|
+
for (const property of node.properties) {
|
|
554
|
+
if (ts.isSpreadAssignment(property)) {
|
|
555
|
+
const spread = evaluateSpread(property.expression, path, ctx, 'object');
|
|
556
|
+
if (spread) {
|
|
557
|
+
Object.assign(out, spread);
|
|
558
|
+
}
|
|
559
|
+
continue;
|
|
560
|
+
}
|
|
561
|
+
const key = property.name ? propertyName(property.name) : undefined;
|
|
562
|
+
if (key === undefined) {
|
|
563
|
+
const keyNode = property.name && ts.isComputedPropertyName(property.name) ? property.name.expression : property;
|
|
564
|
+
const resolved = resolveComputedKey(keyNode, ctx);
|
|
565
|
+
if (resolved === undefined) {
|
|
566
|
+
report(ctx, path, keyNode, 'computed-key', `computed key \`[${snippet(keyNode)}]\` cannot be resolved statically`);
|
|
567
|
+
continue;
|
|
568
|
+
}
|
|
569
|
+
assignProperty(out, resolved, property, path, ctx);
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
assignProperty(out, key, property, path, ctx);
|
|
573
|
+
}
|
|
574
|
+
recordValue(ctx, path, node);
|
|
575
|
+
return out;
|
|
576
|
+
}
|
|
577
|
+
function assignProperty(out, key, property, path, ctx) {
|
|
578
|
+
const child = childPath(path, key);
|
|
579
|
+
let value;
|
|
580
|
+
if (ts.isPropertyAssignment(property)) {
|
|
581
|
+
value = evaluateNode(property.initializer, child, ctx);
|
|
582
|
+
}
|
|
583
|
+
else if (ts.isShorthandPropertyAssignment(property)) {
|
|
584
|
+
value = evaluateNode(property.name, child, ctx);
|
|
585
|
+
}
|
|
586
|
+
else {
|
|
587
|
+
value = report(ctx, child, property, 'function', `\`${key}\` is a method`);
|
|
588
|
+
}
|
|
589
|
+
if (value === OMIT) {
|
|
590
|
+
// `key: undefined` — JSON has no such member; drop it and any position recorded for it
|
|
591
|
+
delete out[key];
|
|
592
|
+
delete ctx.sink.lineMap[child];
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
if (property.name) {
|
|
596
|
+
recordKey(ctx, child, property.name);
|
|
597
|
+
}
|
|
598
|
+
out[key] = value;
|
|
599
|
+
}
|
|
600
|
+
function resolveComputedKey(keyNode, ctx) {
|
|
601
|
+
const inner = unwrap(keyNode);
|
|
602
|
+
if (!isReferenceLike(inner)) {
|
|
603
|
+
return undefined;
|
|
604
|
+
}
|
|
605
|
+
const ref = resolveReference(inner, ctx);
|
|
606
|
+
if (!ref.ok) {
|
|
607
|
+
return undefined;
|
|
608
|
+
}
|
|
609
|
+
if (ref.constant !== undefined) {
|
|
610
|
+
return typeof ref.constant === 'string' || typeof ref.constant === 'number' ? String(ref.constant) : undefined;
|
|
611
|
+
}
|
|
612
|
+
return literalKey(ref.node);
|
|
613
|
+
}
|
|
614
|
+
function evaluateArray(node, path, ctx) {
|
|
615
|
+
const out = [];
|
|
616
|
+
for (const element of node.elements) {
|
|
617
|
+
if (ts.isSpreadElement(element)) {
|
|
618
|
+
const spread = evaluateSpread(element.expression, path, ctx, 'array', out.length);
|
|
619
|
+
if (spread) {
|
|
620
|
+
out.push(...spread);
|
|
621
|
+
}
|
|
622
|
+
continue;
|
|
623
|
+
}
|
|
624
|
+
if (ts.isOmittedExpression(element)) {
|
|
625
|
+
out.push(null);
|
|
626
|
+
continue;
|
|
627
|
+
}
|
|
628
|
+
const value = evaluateNode(element, indexPath(path, out.length), ctx);
|
|
629
|
+
out.push(value === OMIT ? null : value);
|
|
630
|
+
}
|
|
631
|
+
recordValue(ctx, path, node);
|
|
632
|
+
return out;
|
|
633
|
+
}
|
|
634
|
+
function evaluateSpread(expr, path, ctx, expected, base = 0) {
|
|
635
|
+
const text = `...${snippet(expr)}`;
|
|
636
|
+
const ref = resolveReference(expr, ctx);
|
|
637
|
+
if (!ref.ok) {
|
|
638
|
+
report(ctx, path, expr, 'spread', `\`${text}\`: ${ref.reason}`);
|
|
639
|
+
return undefined;
|
|
640
|
+
}
|
|
641
|
+
const target = unwrap(ref.node);
|
|
642
|
+
const matches = expected === 'object' ? ts.isObjectLiteralExpression(target) : ts.isArrayLiteralExpression(target);
|
|
643
|
+
if (!matches || ref.constant !== undefined) {
|
|
644
|
+
report(ctx, path, expr, 'spread', `\`${text}\` is ${describe(target)}; its members are not visible statically`);
|
|
645
|
+
return undefined;
|
|
646
|
+
}
|
|
647
|
+
if (expected === 'object') {
|
|
648
|
+
return withDecls(ctx, ref.decls, () => evaluateObject(target, path, ref.ctx));
|
|
649
|
+
}
|
|
650
|
+
const sub = { lineMap: {}, unverifiable: [] };
|
|
651
|
+
const value = withDecls(ctx, ref.decls, () => evaluateArray(target, path, { ...ref.ctx, sink: sub }));
|
|
652
|
+
mergeShiftedIndices(ctx.sink.lineMap, sub.lineMap, path, base, ctx.sink.unverifiable, sub.unverifiable);
|
|
653
|
+
return value;
|
|
654
|
+
}
|
|
655
|
+
const TARGETS = {
|
|
656
|
+
state: {
|
|
657
|
+
typeNames: ['InitialState', 'AdaptablePersistentState', 'PredefinedConfig'],
|
|
658
|
+
variableNames: ['initialState', 'predefinedConfig'],
|
|
659
|
+
propertyNames: ['initialState', 'predefinedConfig'],
|
|
660
|
+
literal: 'object',
|
|
661
|
+
},
|
|
662
|
+
options: {
|
|
663
|
+
typeNames: ['AdaptableOptions'],
|
|
664
|
+
variableNames: ['adaptableOptions'],
|
|
665
|
+
propertyNames: ['adaptableOptions'],
|
|
666
|
+
wrapper: { argument: 0, attribute: 'adaptableOptions', vueAttributes: ['adaptable-options', 'adaptableOptions'] },
|
|
667
|
+
literal: 'object',
|
|
668
|
+
},
|
|
669
|
+
gridOptions: {
|
|
670
|
+
typeNames: ['GridOptions'],
|
|
671
|
+
variableNames: ['gridOptions'],
|
|
672
|
+
propertyNames: ['gridOptions'],
|
|
673
|
+
wrapper: { argument: 1, attribute: 'gridOptions', vueAttributes: ['grid-options', 'gridOptions'] },
|
|
674
|
+
literal: 'object',
|
|
675
|
+
},
|
|
676
|
+
columnDefs: {
|
|
677
|
+
typeNames: ['ColDef', 'ColGroupDef'],
|
|
678
|
+
variableNames: ['columnDefs'],
|
|
679
|
+
propertyNames: ['columnDefs'],
|
|
680
|
+
literal: 'array',
|
|
681
|
+
},
|
|
682
|
+
};
|
|
683
|
+
/** Rules tried per target, in order (what `tried` reports on a miss) */
|
|
684
|
+
export const LOCATING_RULES = {
|
|
685
|
+
state: ['annotation', 'cast', 'variable', 'property'],
|
|
686
|
+
options: ['annotation', 'cast', 'variable', 'wrapper-argument', 'property'],
|
|
687
|
+
columnDefs: ['annotation', 'variable', 'gridOptions', 'property'],
|
|
688
|
+
};
|
|
689
|
+
function typeMentions(type, names) {
|
|
690
|
+
if (!type) {
|
|
691
|
+
return false;
|
|
692
|
+
}
|
|
693
|
+
// the type itself, possibly wrapped: `T`, `T<A>`, `T[]`, `Array<T>`, `T | undefined`, `(T)`, `import('…').T`;
|
|
694
|
+
// not a type that merely mentions it (`Record<string, T>`, `T['Dashboard']`, `typeof t`)
|
|
695
|
+
const rightmost = (name) => (ts.isIdentifier(name) ? name.text : name.right.text);
|
|
696
|
+
const matches = (node) => {
|
|
697
|
+
if (ts.isParenthesizedTypeNode(node) || ts.isArrayTypeNode(node)) {
|
|
698
|
+
return matches(ts.isArrayTypeNode(node) ? node.elementType : node.type);
|
|
699
|
+
}
|
|
700
|
+
if (ts.isUnionTypeNode(node) || ts.isIntersectionTypeNode(node)) {
|
|
701
|
+
return node.types.some(matches);
|
|
702
|
+
}
|
|
703
|
+
if (ts.isTypeReferenceNode(node)) {
|
|
704
|
+
const name = rightmost(node.typeName);
|
|
705
|
+
if (names.includes(name)) {
|
|
706
|
+
return true;
|
|
707
|
+
}
|
|
708
|
+
return (name === 'Array' || name === 'ReadonlyArray') && !!node.typeArguments?.some(matches);
|
|
709
|
+
}
|
|
710
|
+
if (ts.isImportTypeNode(node)) {
|
|
711
|
+
return !!node.qualifier && names.includes(rightmost(node.qualifier));
|
|
712
|
+
}
|
|
713
|
+
return false;
|
|
714
|
+
};
|
|
715
|
+
return matches(type);
|
|
716
|
+
}
|
|
717
|
+
/** The expressions a function returns: an arrow's expression body, or every `return` not nested in another function */
|
|
718
|
+
function returnedExpressions(fn) {
|
|
719
|
+
if (!fn.body) {
|
|
720
|
+
return [];
|
|
721
|
+
}
|
|
722
|
+
if (!ts.isBlock(fn.body)) {
|
|
723
|
+
return [fn.body];
|
|
724
|
+
}
|
|
725
|
+
const out = [];
|
|
726
|
+
const visit = (node) => {
|
|
727
|
+
if (ts.isReturnStatement(node) && node.expression) {
|
|
728
|
+
out.push(node.expression);
|
|
729
|
+
}
|
|
730
|
+
if (ts.isFunctionLike(node) && node !== fn) {
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
ts.forEachChild(node, visit);
|
|
734
|
+
};
|
|
735
|
+
visit(fn.body);
|
|
736
|
+
return out;
|
|
737
|
+
}
|
|
738
|
+
/** `: T` on a declaration, or `/** @type {T} *\/` in JS */
|
|
739
|
+
function declaredType(node) {
|
|
740
|
+
if (node.type) {
|
|
741
|
+
return node.type;
|
|
742
|
+
}
|
|
743
|
+
const tag = ts.getJSDocTypeTag(node) ?? (ts.isVariableDeclaration(node) ? ts.getJSDocTypeTag(node.parent.parent) : undefined);
|
|
744
|
+
return tag?.typeExpression?.type;
|
|
745
|
+
}
|
|
746
|
+
function walk(sf, visit) {
|
|
747
|
+
const inner = (node) => {
|
|
748
|
+
visit(node);
|
|
749
|
+
ts.forEachChild(node, inner);
|
|
750
|
+
};
|
|
751
|
+
inner(sf);
|
|
752
|
+
}
|
|
753
|
+
function isAdaptableWrapperCall(node) {
|
|
754
|
+
if (ts.isCallExpression(node)) {
|
|
755
|
+
const callee = unwrap(node.expression);
|
|
756
|
+
return ts.isPropertyAccessExpression(callee) && callee.name.text === 'init' && snippet(callee.expression).endsWith('Adaptable');
|
|
757
|
+
}
|
|
758
|
+
if (ts.isNewExpression(node)) {
|
|
759
|
+
return snippet(node.expression).endsWith('Adaptable');
|
|
760
|
+
}
|
|
761
|
+
return false;
|
|
762
|
+
}
|
|
763
|
+
function findNamedNode(ctx, name, pos) {
|
|
764
|
+
const { declarations } = ctx.parsed;
|
|
765
|
+
const variable = pickDeclaration(declarations.variables.get(name), pos);
|
|
766
|
+
if (variable?.initializer) {
|
|
767
|
+
return variable.initializer;
|
|
768
|
+
}
|
|
769
|
+
const property = pickDeclaration(declarations.classProperties.get(name), pos);
|
|
770
|
+
if (property?.initializer) {
|
|
771
|
+
return property.initializer;
|
|
772
|
+
}
|
|
773
|
+
let found;
|
|
774
|
+
walk(ctx.parsed.sf, (node) => {
|
|
775
|
+
if (!found && ts.isPropertyAssignment(node) && propertyName(node.name) === name) {
|
|
776
|
+
found = node.initializer;
|
|
777
|
+
}
|
|
778
|
+
});
|
|
779
|
+
return found;
|
|
780
|
+
}
|
|
781
|
+
/** The node a template-bound expression (`[x]="a.b"`, `:x="a"`) refers to in the script */
|
|
782
|
+
function boundExpression(ctx, text) {
|
|
783
|
+
const parts = text.trim().split('.');
|
|
784
|
+
if (!parts.every((part) => /^[A-Za-z_$][\w$]*$/.test(part))) {
|
|
785
|
+
return undefined;
|
|
786
|
+
}
|
|
787
|
+
let node = findNamedNode(ctx, parts[0], 0);
|
|
788
|
+
for (const part of parts.slice(1)) {
|
|
789
|
+
if (!node) {
|
|
790
|
+
return undefined;
|
|
791
|
+
}
|
|
792
|
+
const ref = resolveReference(node, ctx);
|
|
793
|
+
const target = ref.ok ? unwrap(ref.node) : undefined;
|
|
794
|
+
if (!target || !ts.isObjectLiteralExpression(target)) {
|
|
795
|
+
return undefined;
|
|
796
|
+
}
|
|
797
|
+
const property = target.properties.find((p) => p.name && propertyName(p.name) === part);
|
|
798
|
+
node = property && ts.isPropertyAssignment(property) ? property.initializer : property && ts.isShorthandPropertyAssignment(property) ? property.name : undefined;
|
|
799
|
+
}
|
|
800
|
+
return node;
|
|
801
|
+
}
|
|
802
|
+
function templateBindings(ctx, spec, readTemplate) {
|
|
803
|
+
if (!spec.wrapper) {
|
|
804
|
+
return [];
|
|
805
|
+
}
|
|
806
|
+
const texts = [ctx.parsed.text];
|
|
807
|
+
for (const match of ctx.parsed.text.matchAll(/templateUrl\s*:\s*['"`]([^'"`]+)['"`]/g)) {
|
|
808
|
+
const template = readTemplate(match[1]);
|
|
809
|
+
if (template) {
|
|
810
|
+
texts.push(template);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
const expressions = [];
|
|
814
|
+
const angular = new RegExp(`\\[${spec.wrapper.attribute}\\]\\s*=\\s*["']([^"']+)["']`, 'g');
|
|
815
|
+
const vue = new RegExp(`(?:^|\\s)(?::|v-bind:)(?:${spec.wrapper.vueAttributes.join('|')})\\s*=\\s*["']([^"']+)["']`, 'g');
|
|
816
|
+
for (const text of texts) {
|
|
817
|
+
for (const match of text.matchAll(angular)) {
|
|
818
|
+
expressions.push(match[1]);
|
|
819
|
+
}
|
|
820
|
+
for (const match of text.matchAll(vue)) {
|
|
821
|
+
expressions.push(match[1]);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
return expressions;
|
|
825
|
+
}
|
|
826
|
+
function candidatesFor(spec, rule, ctx, options) {
|
|
827
|
+
const sf = ctx.parsed.sf;
|
|
828
|
+
const out = [];
|
|
829
|
+
const add = (node) => {
|
|
830
|
+
if (node) {
|
|
831
|
+
out.push({ node, locatedBy: rule, ctx });
|
|
832
|
+
}
|
|
833
|
+
};
|
|
834
|
+
switch (rule) {
|
|
835
|
+
case 'annotation':
|
|
836
|
+
walk(sf, (node) => {
|
|
837
|
+
if ((ts.isVariableDeclaration(node) || ts.isPropertyDeclaration(node)) && node.initializer && typeMentions(declaredType(node), spec.typeNames)) {
|
|
838
|
+
add(node.initializer);
|
|
839
|
+
}
|
|
840
|
+
else if ((ts.isArrowFunction(node) || ts.isFunctionExpression(node) || ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node)) &&
|
|
841
|
+
node.type &&
|
|
842
|
+
typeMentions(node.type, spec.typeNames)) {
|
|
843
|
+
// `(): AdaptableOptions => ({ … })` / `function build(): InitialState { return { … }; }`
|
|
844
|
+
returnedExpressions(node).forEach(add);
|
|
845
|
+
}
|
|
846
|
+
});
|
|
847
|
+
break;
|
|
848
|
+
case 'cast':
|
|
849
|
+
walk(sf, (node) => {
|
|
850
|
+
if ((ts.isAsExpression(node) || ts.isSatisfiesExpression(node) || ts.isTypeAssertionExpression(node)) && typeMentions(node.type, spec.typeNames)) {
|
|
851
|
+
add(node.expression);
|
|
852
|
+
}
|
|
853
|
+
});
|
|
854
|
+
break;
|
|
855
|
+
case 'variable':
|
|
856
|
+
walk(sf, (node) => {
|
|
857
|
+
if ((ts.isVariableDeclaration(node) || ts.isPropertyDeclaration(node)) && node.initializer && spec.variableNames.includes(propertyName(node.name) ?? '')) {
|
|
858
|
+
add(node.initializer);
|
|
859
|
+
}
|
|
860
|
+
});
|
|
861
|
+
// an identifier of that name imported from a relative file counts too (`import { columnDefs } from './columnDefs'`)
|
|
862
|
+
for (const name of spec.variableNames) {
|
|
863
|
+
const binding = ctx.parsed.declarations.imports.get(name);
|
|
864
|
+
if (binding && binding.kind !== 'namespace') {
|
|
865
|
+
add(binding.localNode);
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
break;
|
|
869
|
+
case 'wrapper-argument':
|
|
870
|
+
if (spec.wrapper) {
|
|
871
|
+
const { argument, attribute } = spec.wrapper;
|
|
872
|
+
walk(sf, (node) => {
|
|
873
|
+
if (isAdaptableWrapperCall(node)) {
|
|
874
|
+
add(node.arguments?.[argument]);
|
|
875
|
+
}
|
|
876
|
+
else if (ts.isJsxAttribute(node) && ts.isIdentifier(node.name) && node.name.text === attribute && node.initializer) {
|
|
877
|
+
add(ts.isJsxExpression(node.initializer) ? node.initializer.expression : node.initializer);
|
|
878
|
+
}
|
|
879
|
+
});
|
|
880
|
+
for (const expression of templateBindings(ctx, spec, options.readTemplate ?? (() => undefined))) {
|
|
881
|
+
add(boundExpression(ctx, expression));
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
break;
|
|
885
|
+
case 'property':
|
|
886
|
+
walk(sf, (node) => {
|
|
887
|
+
if (ts.isPropertyAssignment(node) && spec.propertyNames.includes(propertyName(node.name) ?? '')) {
|
|
888
|
+
add(node.initializer);
|
|
889
|
+
}
|
|
890
|
+
else if (ts.isShorthandPropertyAssignment(node) && spec.propertyNames.includes(node.name.text)) {
|
|
891
|
+
add(node.name);
|
|
892
|
+
}
|
|
893
|
+
});
|
|
894
|
+
break;
|
|
895
|
+
case 'gridOptions': {
|
|
896
|
+
const grid = locateLiteral(TARGETS.gridOptions, ['annotation', 'cast', 'variable', 'wrapper-argument', 'property'], ctx, options);
|
|
897
|
+
if (grid) {
|
|
898
|
+
const ref = resolveReference(grid.node, grid.ctx);
|
|
899
|
+
const target = ref.ok ? unwrap(ref.node) : undefined;
|
|
900
|
+
if (target && ts.isObjectLiteralExpression(target)) {
|
|
901
|
+
const property = target.properties.find((p) => p.name && propertyName(p.name) === 'columnDefs');
|
|
902
|
+
if (property && ts.isPropertyAssignment(property)) {
|
|
903
|
+
out.push({ node: property.initializer, locatedBy: rule, ctx: ref.ok ? ref.ctx : ctx });
|
|
904
|
+
}
|
|
905
|
+
else if (property && ts.isShorthandPropertyAssignment(property)) {
|
|
906
|
+
out.push({ node: property.name, locatedBy: rule, ctx: ref.ok ? ref.ctx : ctx });
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
break;
|
|
911
|
+
}
|
|
912
|
+
case 'json':
|
|
913
|
+
break;
|
|
914
|
+
}
|
|
915
|
+
return out;
|
|
916
|
+
}
|
|
917
|
+
function isLiteralOf(node, kind) {
|
|
918
|
+
return kind === 'object' ? ts.isObjectLiteralExpression(node) : ts.isArrayLiteralExpression(node);
|
|
919
|
+
}
|
|
920
|
+
/**
|
|
921
|
+
* The first candidate (in rule order) that resolves to a literal of the expected
|
|
922
|
+
* kind; failing that, the first candidate of any rule — so a State built by a call
|
|
923
|
+
* is still reported, as `unverifiable` at the root, rather than "not found".
|
|
924
|
+
*/
|
|
925
|
+
function locateLiteral(spec, rules, ctx, options) {
|
|
926
|
+
let fallback;
|
|
927
|
+
for (const rule of rules) {
|
|
928
|
+
for (const candidate of candidatesFor(spec, rule, ctx, options)) {
|
|
929
|
+
const ref = resolveReference(candidate.node, candidate.ctx);
|
|
930
|
+
if (ref.ok && ref.constant === undefined && isLiteralOf(unwrap(ref.node), spec.literal)) {
|
|
931
|
+
return candidate;
|
|
932
|
+
}
|
|
933
|
+
fallback ??= candidate;
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
return fallback;
|
|
937
|
+
}
|
|
938
|
+
/** Locates and evaluates the target literal in a parsed file */
|
|
939
|
+
export function extractLiteral(parsed, target, ctx, options = {}) {
|
|
940
|
+
const rules = LOCATING_RULES[target];
|
|
941
|
+
const candidate = locateLiteral(TARGETS[target], rules, ctx, options);
|
|
942
|
+
if (!candidate) {
|
|
943
|
+
return { value: undefined, file: parsed.file, target, tried: rules };
|
|
944
|
+
}
|
|
945
|
+
const value = evaluate(candidate.node, ROOT_PATH, candidate.ctx);
|
|
946
|
+
const root = ctx.sink.lineMap[ROOT_PATH]?.value ?? positionOf(candidate.ctx, candidate.node);
|
|
947
|
+
return {
|
|
948
|
+
value,
|
|
949
|
+
lineMap: ctx.sink.lineMap,
|
|
950
|
+
unverifiable: ctx.sink.unverifiable,
|
|
951
|
+
source: { file: root.file, locatedBy: candidate.locatedBy, line: root.line, column: root.column },
|
|
952
|
+
};
|
|
953
|
+
}
|
|
954
|
+
/** Evaluates a parsed JSON document (already validated by JSON5) for its line map */
|
|
955
|
+
export function jsonLineMap(parsed) {
|
|
956
|
+
const ctx = createContext(parsed);
|
|
957
|
+
const statement = parsed.sf.statements[0];
|
|
958
|
+
if (statement && ts.isExpressionStatement(statement)) {
|
|
959
|
+
evaluate(statement.expression, ROOT_PATH, ctx);
|
|
960
|
+
}
|
|
961
|
+
return ctx.sink;
|
|
962
|
+
}
|
|
963
|
+
//# sourceMappingURL=ts-literal.js.map
|