@endora-commerce/cli 0.100.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +37 -0
- package/dist/bin/endora.d.ts +28 -0
- package/dist/bin/endora.d.ts.map +1 -0
- package/dist/bin/endora.js +926 -0
- package/dist/bin/endora.js.map +1 -0
- package/dist/check/estate.d.ts +189 -0
- package/dist/check/estate.d.ts.map +1 -0
- package/dist/check/estate.js +1037 -0
- package/dist/check/estate.js.map +1 -0
- package/dist/check/hosts.d.ts +25 -0
- package/dist/check/hosts.d.ts.map +1 -0
- package/dist/check/hosts.js +1347 -0
- package/dist/check/hosts.js.map +1 -0
- package/dist/check/index.d.ts +60 -0
- package/dist/check/index.d.ts.map +1 -0
- package/dist/check/index.js +111 -0
- package/dist/check/index.js.map +1 -0
- package/dist/check/layout.d.ts +136 -0
- package/dist/check/layout.d.ts.map +1 -0
- package/dist/check/layout.js +262 -0
- package/dist/check/layout.js.map +1 -0
- package/dist/check/ledger.d.ts +98 -0
- package/dist/check/ledger.d.ts.map +1 -0
- package/dist/check/ledger.js +173 -0
- package/dist/check/ledger.js.map +1 -0
- package/dist/check/peer-owners.d.ts +116 -0
- package/dist/check/peer-owners.d.ts.map +1 -0
- package/dist/check/peer-owners.js +225 -0
- package/dist/check/peer-owners.js.map +1 -0
- package/dist/check/report.d.ts +33 -0
- package/dist/check/report.d.ts.map +1 -0
- package/dist/check/report.js +107 -0
- package/dist/check/report.js.map +1 -0
- package/dist/check/run.d.ts +147 -0
- package/dist/check/run.d.ts.map +1 -0
- package/dist/check/run.js +111 -0
- package/dist/check/run.js.map +1 -0
- package/dist/checks.d.ts +17 -0
- package/dist/checks.d.ts.map +1 -0
- package/dist/checks.js +17 -0
- package/dist/checks.js.map +1 -0
- package/dist/dev/index.d.ts +83 -0
- package/dist/dev/index.d.ts.map +1 -0
- package/dist/dev/index.js +298 -0
- package/dist/dev/index.js.map +1 -0
- package/dist/generate/divergence.d.ts +38 -0
- package/dist/generate/divergence.d.ts.map +1 -0
- package/dist/generate/divergence.js +237 -0
- package/dist/generate/divergence.js.map +1 -0
- package/dist/generate/index.d.ts +90 -0
- package/dist/generate/index.d.ts.map +1 -0
- package/dist/generate/index.js +369 -0
- package/dist/generate/index.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/dist/inputs/declaration.d.ts +18 -0
- package/dist/inputs/declaration.d.ts.map +1 -0
- package/dist/inputs/declaration.js +64 -0
- package/dist/inputs/declaration.js.map +1 -0
- package/dist/inputs/env-file.d.ts +73 -0
- package/dist/inputs/env-file.d.ts.map +1 -0
- package/dist/inputs/env-file.js +134 -0
- package/dist/inputs/env-file.js.map +1 -0
- package/dist/inputs/prompt.d.ts +21 -0
- package/dist/inputs/prompt.d.ts.map +1 -0
- package/dist/inputs/prompt.js +59 -0
- package/dist/inputs/prompt.js.map +1 -0
- package/dist/inputs/resolve.d.ts +163 -0
- package/dist/inputs/resolve.d.ts.map +1 -0
- package/dist/inputs/resolve.js +290 -0
- package/dist/inputs/resolve.js.map +1 -0
- package/dist/install/host.d.ts +27 -0
- package/dist/install/host.d.ts.map +1 -0
- package/dist/install/host.js +90 -0
- package/dist/install/host.js.map +1 -0
- package/dist/install/index.d.ts +173 -0
- package/dist/install/index.d.ts.map +1 -0
- package/dist/install/index.js +793 -0
- package/dist/install/index.js.map +1 -0
- package/dist/install/wizard.d.ts +144 -0
- package/dist/install/wizard.d.ts.map +1 -0
- package/dist/install/wizard.js +362 -0
- package/dist/install/wizard.js.map +1 -0
- package/dist/lib/admin-artefacts.d.ts +70 -0
- package/dist/lib/admin-artefacts.d.ts.map +1 -0
- package/dist/lib/admin-artefacts.js +354 -0
- package/dist/lib/admin-artefacts.js.map +1 -0
- package/dist/lib/admin-surfaces.d.ts +298 -0
- package/dist/lib/admin-surfaces.d.ts.map +1 -0
- package/dist/lib/admin-surfaces.js +669 -0
- package/dist/lib/admin-surfaces.js.map +1 -0
- package/dist/lib/delegated-composer.d.ts +85 -0
- package/dist/lib/delegated-composer.d.ts.map +1 -0
- package/dist/lib/delegated-composer.js +241 -0
- package/dist/lib/delegated-composer.js.map +1 -0
- package/dist/lib/divergence-artefacts.d.ts +305 -0
- package/dist/lib/divergence-artefacts.d.ts.map +1 -0
- package/dist/lib/divergence-artefacts.js +828 -0
- package/dist/lib/divergence-artefacts.js.map +1 -0
- package/dist/lib/divergence.d.ts +337 -0
- package/dist/lib/divergence.d.ts.map +1 -0
- package/dist/lib/divergence.js +1005 -0
- package/dist/lib/divergence.js.map +1 -0
- package/dist/lib/docs-artefacts.d.ts +395 -0
- package/dist/lib/docs-artefacts.d.ts.map +1 -0
- package/dist/lib/docs-artefacts.js +781 -0
- package/dist/lib/docs-artefacts.js.map +1 -0
- package/dist/lib/emitted-exports.d.ts +21 -0
- package/dist/lib/emitted-exports.d.ts.map +1 -0
- package/dist/lib/emitted-exports.js +96 -0
- package/dist/lib/emitted-exports.js.map +1 -0
- package/dist/lib/emitted-freshness.d.ts +112 -0
- package/dist/lib/emitted-freshness.d.ts.map +1 -0
- package/dist/lib/emitted-freshness.js +288 -0
- package/dist/lib/emitted-freshness.js.map +1 -0
- package/dist/lib/entity-index-artefact.d.ts +95 -0
- package/dist/lib/entity-index-artefact.d.ts.map +1 -0
- package/dist/lib/entity-index-artefact.js +210 -0
- package/dist/lib/entity-index-artefact.js.map +1 -0
- package/dist/lib/instance-build-inputs.d.ts +108 -0
- package/dist/lib/instance-build-inputs.d.ts.map +1 -0
- package/dist/lib/instance-build-inputs.js +165 -0
- package/dist/lib/instance-build-inputs.js.map +1 -0
- package/dist/lib/module-docs.d.ts +473 -0
- package/dist/lib/module-docs.d.ts.map +1 -0
- package/dist/lib/module-docs.js +711 -0
- package/dist/lib/module-docs.js.map +1 -0
- package/dist/lib/module-package-subpaths.d.ts +56 -0
- package/dist/lib/module-package-subpaths.d.ts.map +1 -0
- package/dist/lib/module-package-subpaths.js +223 -0
- package/dist/lib/module-package-subpaths.js.map +1 -0
- package/dist/lib/module-packages.d.ts +200 -0
- package/dist/lib/module-packages.d.ts.map +1 -0
- package/dist/lib/module-packages.js +580 -0
- package/dist/lib/module-packages.js.map +1 -0
- package/dist/lib/module-population.d.ts +129 -0
- package/dist/lib/module-population.d.ts.map +1 -0
- package/dist/lib/module-population.js +172 -0
- package/dist/lib/module-population.js.map +1 -0
- package/dist/lib/module-roots.d.ts +337 -0
- package/dist/lib/module-roots.d.ts.map +1 -0
- package/dist/lib/module-roots.js +586 -0
- package/dist/lib/module-roots.js.map +1 -0
- package/dist/lib/nested-checkouts.d.ts +33 -0
- package/dist/lib/nested-checkouts.d.ts.map +1 -0
- package/dist/lib/nested-checkouts.js +160 -0
- package/dist/lib/nested-checkouts.js.map +1 -0
- package/dist/lib/platform-root.d.ts +43 -0
- package/dist/lib/platform-root.d.ts.map +1 -0
- package/dist/lib/platform-root.js +134 -0
- package/dist/lib/platform-root.js.map +1 -0
- package/dist/lib/platform-surface.d.ts +235 -0
- package/dist/lib/platform-surface.d.ts.map +1 -0
- package/dist/lib/platform-surface.js +393 -0
- package/dist/lib/platform-surface.js.map +1 -0
- package/dist/lib/port-registrations.d.ts +223 -0
- package/dist/lib/port-registrations.d.ts.map +1 -0
- package/dist/lib/port-registrations.js +532 -0
- package/dist/lib/port-registrations.js.map +1 -0
- package/dist/lib/read-size.d.ts +154 -0
- package/dist/lib/read-size.d.ts.map +1 -0
- package/dist/lib/read-size.js +182 -0
- package/dist/lib/read-size.js.map +1 -0
- package/dist/lib/registration-owners.d.ts +79 -0
- package/dist/lib/registration-owners.d.ts.map +1 -0
- package/dist/lib/registration-owners.js +77 -0
- package/dist/lib/registration-owners.js.map +1 -0
- package/dist/lib/release-index.d.ts +53 -0
- package/dist/lib/release-index.d.ts.map +1 -0
- package/dist/lib/release-index.js +162 -0
- package/dist/lib/release-index.js.map +1 -0
- package/dist/lib/repeating-timers.d.ts +79 -0
- package/dist/lib/repeating-timers.d.ts.map +1 -0
- package/dist/lib/repeating-timers.js +189 -0
- package/dist/lib/repeating-timers.js.map +1 -0
- package/dist/lib/source-text.d.ts +34 -0
- package/dist/lib/source-text.d.ts.map +1 -0
- package/dist/lib/source-text.js +80 -0
- package/dist/lib/source-text.js.map +1 -0
- package/dist/lib/specifiers.d.ts +20 -0
- package/dist/lib/specifiers.d.ts.map +1 -0
- package/dist/lib/specifiers.js +130 -0
- package/dist/lib/specifiers.js.map +1 -0
- package/dist/lib/sql-tables.d.ts +166 -0
- package/dist/lib/sql-tables.d.ts.map +1 -0
- package/dist/lib/sql-tables.js +464 -0
- package/dist/lib/sql-tables.js.map +1 -0
- package/dist/lib/switchable-modules.d.ts +54 -0
- package/dist/lib/switchable-modules.d.ts.map +1 -0
- package/dist/lib/switchable-modules.js +104 -0
- package/dist/lib/switchable-modules.js.map +1 -0
- package/dist/lib/tailwind-sources.d.ts +136 -0
- package/dist/lib/tailwind-sources.d.ts.map +1 -0
- package/dist/lib/tailwind-sources.js +307 -0
- package/dist/lib/tailwind-sources.js.map +1 -0
- package/dist/lib/ui-layer.d.ts +54 -0
- package/dist/lib/ui-layer.d.ts.map +1 -0
- package/dist/lib/ui-layer.js +57 -0
- package/dist/lib/ui-layer.js.map +1 -0
- package/dist/lib/workspace-packages.d.ts +186 -0
- package/dist/lib/workspace-packages.d.ts.map +1 -0
- package/dist/lib/workspace-packages.js +351 -0
- package/dist/lib/workspace-packages.js.map +1 -0
- package/dist/new-instance/deploy.d.ts +211 -0
- package/dist/new-instance/deploy.d.ts.map +1 -0
- package/dist/new-instance/deploy.js +1381 -0
- package/dist/new-instance/deploy.js.map +1 -0
- package/dist/new-instance/docs-toolchain.d.ts +66 -0
- package/dist/new-instance/docs-toolchain.d.ts.map +1 -0
- package/dist/new-instance/docs-toolchain.js +69 -0
- package/dist/new-instance/docs-toolchain.js.map +1 -0
- package/dist/new-instance/host.d.ts +124 -0
- package/dist/new-instance/host.d.ts.map +1 -0
- package/dist/new-instance/host.js +276 -0
- package/dist/new-instance/host.js.map +1 -0
- package/dist/new-instance/index.d.ts +118 -0
- package/dist/new-instance/index.d.ts.map +1 -0
- package/dist/new-instance/index.js +567 -0
- package/dist/new-instance/index.js.map +1 -0
- package/dist/new-instance/modules.d.ts +188 -0
- package/dist/new-instance/modules.d.ts.map +1 -0
- package/dist/new-instance/modules.js +392 -0
- package/dist/new-instance/modules.js.map +1 -0
- package/dist/new-instance/template.d.ts +505 -0
- package/dist/new-instance/template.d.ts.map +1 -0
- package/dist/new-instance/template.js +1886 -0
- package/dist/new-instance/template.js.map +1 -0
- package/dist/new-module/emit.d.ts +67 -0
- package/dist/new-module/emit.d.ts.map +1 -0
- package/dist/new-module/emit.js +1393 -0
- package/dist/new-module/emit.js.map +1 -0
- package/dist/new-module/host.d.ts +67 -0
- package/dist/new-module/host.d.ts.map +1 -0
- package/dist/new-module/host.js +224 -0
- package/dist/new-module/host.js.map +1 -0
- package/dist/new-module/index.d.ts +32 -0
- package/dist/new-module/index.d.ts.map +1 -0
- package/dist/new-module/index.js +193 -0
- package/dist/new-module/index.js.map +1 -0
- package/dist/new-module/spec.d.ts +188 -0
- package/dist/new-module/spec.d.ts.map +1 -0
- package/dist/new-module/spec.js +404 -0
- package/dist/new-module/spec.js.map +1 -0
- package/dist/new-module/text.d.ts +15 -0
- package/dist/new-module/text.d.ts.map +1 -0
- package/dist/new-module/text.js +22 -0
- package/dist/new-module/text.js.map +1 -0
- package/dist/new-storefront/dockerfile.d.ts +20 -0
- package/dist/new-storefront/dockerfile.d.ts.map +1 -0
- package/dist/new-storefront/dockerfile.js +131 -0
- package/dist/new-storefront/dockerfile.js.map +1 -0
- package/dist/new-storefront/gitignore.d.ts +23 -0
- package/dist/new-storefront/gitignore.d.ts.map +1 -0
- package/dist/new-storefront/gitignore.js +39 -0
- package/dist/new-storefront/gitignore.js.map +1 -0
- package/dist/new-storefront/index.d.ts +93 -0
- package/dist/new-storefront/index.d.ts.map +1 -0
- package/dist/new-storefront/index.js +329 -0
- package/dist/new-storefront/index.js.map +1 -0
- package/dist/new-storefront/npmrc.d.ts +98 -0
- package/dist/new-storefront/npmrc.d.ts.map +1 -0
- package/dist/new-storefront/npmrc.js +217 -0
- package/dist/new-storefront/npmrc.js.map +1 -0
- package/dist/new-storefront/reference.d.ts +189 -0
- package/dist/new-storefront/reference.d.ts.map +1 -0
- package/dist/new-storefront/reference.js +430 -0
- package/dist/new-storefront/reference.js.map +1 -0
- package/dist/new-storefront/rewrite.d.ts +171 -0
- package/dist/new-storefront/rewrite.d.ts.map +1 -0
- package/dist/new-storefront/rewrite.js +701 -0
- package/dist/new-storefront/rewrite.js.map +1 -0
- package/dist/release-index.json +284 -0
- package/dist/rules/action-route-permissions.d.ts +141 -0
- package/dist/rules/action-route-permissions.d.ts.map +1 -0
- package/dist/rules/action-route-permissions.js +556 -0
- package/dist/rules/action-route-permissions.js.map +1 -0
- package/dist/rules/bundle-pairing.d.ts +74 -0
- package/dist/rules/bundle-pairing.d.ts.map +1 -0
- package/dist/rules/bundle-pairing.js +281 -0
- package/dist/rules/bundle-pairing.js.map +1 -0
- package/dist/rules/channel-resolution.d.ts +17 -0
- package/dist/rules/channel-resolution.d.ts.map +1 -0
- package/dist/rules/channel-resolution.js +382 -0
- package/dist/rules/channel-resolution.js.map +1 -0
- package/dist/rules/command-coverage.d.ts +210 -0
- package/dist/rules/command-coverage.d.ts.map +1 -0
- package/dist/rules/command-coverage.js +714 -0
- package/dist/rules/command-coverage.js.map +1 -0
- package/dist/rules/container-imports.d.ts +60 -0
- package/dist/rules/container-imports.d.ts.map +1 -0
- package/dist/rules/container-imports.js +158 -0
- package/dist/rules/container-imports.js.map +1 -0
- package/dist/rules/default-language-prose.d.ts +212 -0
- package/dist/rules/default-language-prose.d.ts.map +1 -0
- package/dist/rules/default-language-prose.js +710 -0
- package/dist/rules/default-language-prose.js.map +1 -0
- package/dist/rules/diacritic-folds.d.ts +238 -0
- package/dist/rules/diacritic-folds.d.ts.map +1 -0
- package/dist/rules/diacritic-folds.js +681 -0
- package/dist/rules/diacritic-folds.js.map +1 -0
- package/dist/rules/entity-tenant-classification.d.ts +171 -0
- package/dist/rules/entity-tenant-classification.d.ts.map +1 -0
- package/dist/rules/entity-tenant-classification.js +323 -0
- package/dist/rules/entity-tenant-classification.js.map +1 -0
- package/dist/rules/entry-presence.d.ts +142 -0
- package/dist/rules/entry-presence.d.ts.map +1 -0
- package/dist/rules/entry-presence.js +339 -0
- package/dist/rules/entry-presence.js.map +1 -0
- package/dist/rules/entry-scope.d.ts +91 -0
- package/dist/rules/entry-scope.d.ts.map +1 -0
- package/dist/rules/entry-scope.js +404 -0
- package/dist/rules/entry-scope.js.map +1 -0
- package/dist/rules/env-inputs.d.ts +222 -0
- package/dist/rules/env-inputs.d.ts.map +1 -0
- package/dist/rules/env-inputs.js +951 -0
- package/dist/rules/env-inputs.js.map +1 -0
- package/dist/rules/kernel-boundary.d.ts +37 -0
- package/dist/rules/kernel-boundary.d.ts.map +1 -0
- package/dist/rules/kernel-boundary.js +195 -0
- package/dist/rules/kernel-boundary.js.map +1 -0
- package/dist/rules/nul-bytes.d.ts +233 -0
- package/dist/rules/nul-bytes.d.ts.map +1 -0
- package/dist/rules/nul-bytes.js +332 -0
- package/dist/rules/nul-bytes.js.map +1 -0
- package/dist/rules/platform-surface.d.ts +479 -0
- package/dist/rules/platform-surface.d.ts.map +1 -0
- package/dist/rules/platform-surface.js +749 -0
- package/dist/rules/platform-surface.js.map +1 -0
- package/dist/rules/port-catches.d.ts +225 -0
- package/dist/rules/port-catches.d.ts.map +1 -0
- package/dist/rules/port-catches.js +1374 -0
- package/dist/rules/port-catches.js.map +1 -0
- package/dist/rules/port-shape.d.ts +213 -0
- package/dist/rules/port-shape.d.ts.map +1 -0
- package/dist/rules/port-shape.js +670 -0
- package/dist/rules/port-shape.js.map +1 -0
- package/dist/rules/queue-names.d.ts +108 -0
- package/dist/rules/queue-names.d.ts.map +1 -0
- package/dist/rules/queue-names.js +395 -0
- package/dist/rules/queue-names.js.map +1 -0
- package/dist/rules/singleton-identity.d.ts +205 -0
- package/dist/rules/singleton-identity.d.ts.map +1 -0
- package/dist/rules/singleton-identity.js +830 -0
- package/dist/rules/singleton-identity.js.map +1 -0
- package/dist/rules/subscribe-seam.d.ts +121 -0
- package/dist/rules/subscribe-seam.d.ts.map +1 -0
- package/dist/rules/subscribe-seam.js +594 -0
- package/dist/rules/subscribe-seam.js.map +1 -0
- package/dist/rules/transaction-context.d.ts +40 -0
- package/dist/rules/transaction-context.d.ts.map +1 -0
- package/dist/rules/transaction-context.js +294 -0
- package/dist/rules/transaction-context.js.map +1 -0
- package/package.json +59 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The release index — what this CLI's release published, written at build
|
|
3
|
+
* (`specs/080-f4-real-scope/rulings.md` D-271 clause 2).
|
|
4
|
+
*
|
|
5
|
+
* ## Why the CLI carries it
|
|
6
|
+
*
|
|
7
|
+
* `endora install` run where nothing of ours is installed — the `npx` front
|
|
8
|
+
* door, from an empty directory — has no module manifest to read: the platform,
|
|
9
|
+
* the admin packages and every module are what the module set is derived from,
|
|
10
|
+
* and none of them can be read without installing them. The install verb
|
|
11
|
+
* therefore provisions a temporary host from this index (`install/host.ts`).
|
|
12
|
+
* It is a fact the CLI publishes **about its own release**, so it is
|
|
13
|
+
* `cli-product.md` R2.3's *"the CLI's own manifest"* class rather than a
|
|
14
|
+
* registry query or a list.
|
|
15
|
+
*
|
|
16
|
+
* ## Derived, and never written by hand
|
|
17
|
+
*
|
|
18
|
+
* {@link publishablePackages} is the one rule: every workspace package under
|
|
19
|
+
* `packages/` whose manifest is not `private` and has a name. The instance
|
|
20
|
+
* acceptance criterion packs exactly that population and imports this function
|
|
21
|
+
* rather than restating it; the index is the same population narrowed to the
|
|
22
|
+
* CLI's own scope, each at the `version` its manifest declares at build time.
|
|
23
|
+
* It lives in `dist` and is never committed, so nothing in the tree can go
|
|
24
|
+
* stale: the publish job builds the versioned tree immediately before it
|
|
25
|
+
* publishes, and a release that is not uniform is recorded as it is. A paid
|
|
26
|
+
* package is in no index because it is not in this workspace (D-265).
|
|
27
|
+
*/
|
|
28
|
+
import { existsSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
29
|
+
import { dirname, join } from 'node:path';
|
|
30
|
+
import { fileURLToPath } from 'node:url';
|
|
31
|
+
/** The file's name under the CLI's `dist`. */
|
|
32
|
+
export const RELEASE_INDEX_FILE = 'release-index.json';
|
|
33
|
+
/**
|
|
34
|
+
* Every publishable workspace package under `packagesRoot`, with its directory.
|
|
35
|
+
*
|
|
36
|
+
* A directory holding a `package.json` is a package and is not descended
|
|
37
|
+
* into; one without is a grouping directory (`packages/modules/`) and is. A
|
|
38
|
+
* manifest declaring `private: true`, or no `name`, is not publishable.
|
|
39
|
+
*/
|
|
40
|
+
export function publishablePackages(packagesRoot) {
|
|
41
|
+
const found = [];
|
|
42
|
+
const walk = (root) => {
|
|
43
|
+
if (!existsSync(root))
|
|
44
|
+
return;
|
|
45
|
+
for (const entry of readdirSync(root, { withFileTypes: true })) {
|
|
46
|
+
if (!entry.isDirectory())
|
|
47
|
+
continue;
|
|
48
|
+
const dir = join(root, entry.name);
|
|
49
|
+
const manifestPath = join(dir, 'package.json');
|
|
50
|
+
if (!existsSync(manifestPath)) {
|
|
51
|
+
walk(dir);
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
|
|
55
|
+
if (manifest.private === true || typeof manifest.name !== 'string')
|
|
56
|
+
continue;
|
|
57
|
+
found.push({
|
|
58
|
+
name: manifest.name,
|
|
59
|
+
dir,
|
|
60
|
+
version: typeof manifest.version === 'string' ? manifest.version : undefined,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
walk(packagesRoot);
|
|
65
|
+
return found;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The index over `packagesRoot`, narrowed to `scope` (`@endora-commerce/`).
|
|
69
|
+
*
|
|
70
|
+
* A publishable package that declares no version is a throw rather than a
|
|
71
|
+
* skip: an index short by a package is a host short by a module, and the
|
|
72
|
+
* module set derived from it would be silently smaller than the release.
|
|
73
|
+
*/
|
|
74
|
+
export function releaseIndexOf(packagesRoot, scope) {
|
|
75
|
+
const packages = [];
|
|
76
|
+
for (const pkg of publishablePackages(packagesRoot)) {
|
|
77
|
+
if (!pkg.name.startsWith(scope))
|
|
78
|
+
continue;
|
|
79
|
+
if (pkg.version === undefined) {
|
|
80
|
+
throw new Error(`${join(pkg.dir, 'package.json')} is publishable and declares no \`version\`, so the ` +
|
|
81
|
+
'release index cannot say which version of it this release carries.');
|
|
82
|
+
}
|
|
83
|
+
packages.push({ name: pkg.name, version: pkg.version });
|
|
84
|
+
}
|
|
85
|
+
packages.sort((a, b) => a.name.localeCompare(b.name));
|
|
86
|
+
return { packages };
|
|
87
|
+
}
|
|
88
|
+
/** Read an index back, refusing anything that is not one. `source` names the file. */
|
|
89
|
+
export function parseReleaseIndex(text, source) {
|
|
90
|
+
let parsed;
|
|
91
|
+
try {
|
|
92
|
+
parsed = JSON.parse(text);
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
throw new Error(`${source} is not JSON (${error instanceof Error ? error.message : String(error)}).`);
|
|
96
|
+
}
|
|
97
|
+
const packages = parsed?.packages;
|
|
98
|
+
if (!Array.isArray(packages)) {
|
|
99
|
+
throw new Error(`${source} holds no \`packages\` list.`);
|
|
100
|
+
}
|
|
101
|
+
const entries = [];
|
|
102
|
+
for (const entry of packages) {
|
|
103
|
+
const name = entry?.name;
|
|
104
|
+
const version = entry?.version;
|
|
105
|
+
if (typeof name !== 'string' || typeof version !== 'string') {
|
|
106
|
+
throw new Error(`${source} holds an entry with no string \`name\` or \`version\`.`);
|
|
107
|
+
}
|
|
108
|
+
entries.push({ name, version });
|
|
109
|
+
}
|
|
110
|
+
if (entries.length === 0) {
|
|
111
|
+
throw new Error(`${source} names no package, so there is nothing to install from it.`);
|
|
112
|
+
}
|
|
113
|
+
return { packages: entries };
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Where this CLI's own index is: `dist/` of the nearest package root above
|
|
117
|
+
* this module — the same file from `src/` in a checkout and from `dist/` in an
|
|
118
|
+
* installed tarball.
|
|
119
|
+
*/
|
|
120
|
+
export function ownReleaseIndexPath(moduleUrl = import.meta.url) {
|
|
121
|
+
let current = dirname(fileURLToPath(moduleUrl));
|
|
122
|
+
for (;;) {
|
|
123
|
+
if (existsSync(join(current, 'package.json')))
|
|
124
|
+
return join(current, 'dist', RELEASE_INDEX_FILE);
|
|
125
|
+
const parent = dirname(current);
|
|
126
|
+
if (parent === current)
|
|
127
|
+
return join(dirname(fileURLToPath(moduleUrl)), RELEASE_INDEX_FILE);
|
|
128
|
+
current = parent;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* The build step: derive the index for the CLI package at `cliDir` and write
|
|
133
|
+
* it into its `dist`.
|
|
134
|
+
*
|
|
135
|
+
* The workspace is found by walking up to `pnpm-workspace.yaml`, and the
|
|
136
|
+
* scope comes off the CLI's own name — nothing here is written down. Called by
|
|
137
|
+
* `scripts/write-release-index.mjs`, which the package's `build` runs after
|
|
138
|
+
* `tsc`; a failure fails the build rather than shipping a CLI with no index.
|
|
139
|
+
*/
|
|
140
|
+
export function writeReleaseIndex(cliDir) {
|
|
141
|
+
const own = JSON.parse(readFileSync(join(cliDir, 'package.json'), 'utf8'));
|
|
142
|
+
const scope = typeof own.name === 'string' ? /^(@[^/]+\/)/.exec(own.name)?.[1] : undefined;
|
|
143
|
+
if (scope === undefined) {
|
|
144
|
+
throw new Error(`${join(cliDir, 'package.json')} declares no scoped name to derive a scope from.`);
|
|
145
|
+
}
|
|
146
|
+
let root = cliDir;
|
|
147
|
+
while (!existsSync(join(root, 'pnpm-workspace.yaml'))) {
|
|
148
|
+
const parent = dirname(root);
|
|
149
|
+
if (parent === root) {
|
|
150
|
+
throw new Error(`no pnpm-workspace.yaml above ${cliDir}: the release index is derived from a checkout.`);
|
|
151
|
+
}
|
|
152
|
+
root = parent;
|
|
153
|
+
}
|
|
154
|
+
const index = releaseIndexOf(join(root, 'packages'), scope);
|
|
155
|
+
const platform = `${scope}platform`;
|
|
156
|
+
if (!index.packages.some((entry) => entry.name === platform)) {
|
|
157
|
+
throw new Error(`${platform} is not among the publishable packages under ${join(root, 'packages')}.`);
|
|
158
|
+
}
|
|
159
|
+
writeFileSync(join(cliDir, 'dist', RELEASE_INDEX_FILE), `${JSON.stringify(index, null, 2)}\n`, 'utf8');
|
|
160
|
+
return index;
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=release-index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"release-index.js","sourceRoot":"","sources":["../../src/lib/release-index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one recognizer for "this file starts a repeating execution of its own"
|
|
3
|
+
* (issue #128).
|
|
4
|
+
*
|
|
5
|
+
* Two checks ask that question and used to answer it separately.
|
|
6
|
+
* `check-entry-presence.ts` reads the callback and knows a repeating timer is
|
|
7
|
+
* not only a `setInterval`: a `setTimeout` whose callback re-arms a timer is the
|
|
8
|
+
* same entry point wearing a different constructor. `check-entry-scope.ts`
|
|
9
|
+
* grepped for `setInterval(` — so `search`'s reindex loop, which is exactly that
|
|
10
|
+
* `setTimeout`, was never in its population, and the `interval=4` it printed
|
|
11
|
+
* every run read as a settled count rather than as one spelling's worth of
|
|
12
|
+
* coverage.
|
|
13
|
+
*
|
|
14
|
+
* The fix is a shared recognizer rather than a second one. Lending only the last
|
|
15
|
+
* predicate (`isSelfRescheduling`) would have left the caller to re-implement the
|
|
16
|
+
* parse, the callback binding and the enclosing-function name — most of the
|
|
17
|
+
* recognizer — and those are precisely the parts that drift. So the whole shape
|
|
18
|
+
* lives here and both checks consume it; neither owns it.
|
|
19
|
+
*
|
|
20
|
+
* What this deliberately does **not** decide: what to do about a site it finds.
|
|
21
|
+
* `check-entry-presence` asks whether the callback decides presence,
|
|
22
|
+
* `check-entry-scope` whether the file opens a scope. The shapes are shared, the
|
|
23
|
+
* rules are not.
|
|
24
|
+
*
|
|
25
|
+
* ## Limits, and why they are the same for both callers
|
|
26
|
+
*
|
|
27
|
+
* - A one-shot `setTimeout` — an `AbortController` deadline, a sleep between
|
|
28
|
+
* retries — is not a repeating entry point and is not reported.
|
|
29
|
+
* - A callback that is an identifier bound in another file cannot be read, so
|
|
30
|
+
* a `setTimeout` reached that way is invisible. A `setInterval` is reported
|
|
31
|
+
* anyway, with a null callback: it repeats whatever its callback turns out to
|
|
32
|
+
* be, and what a check cannot see it must not vouch for.
|
|
33
|
+
* - A scheduler hidden behind an imported helper is invisible. The analysis is
|
|
34
|
+
* single-file and syntactic; if that shape arrives, this module has to grow.
|
|
35
|
+
*/
|
|
36
|
+
import ts from 'typescript';
|
|
37
|
+
/** The scheduling call a site was built from. */
|
|
38
|
+
export type RepeatingTimerConstruct = 'setInterval' | 'setTimeout';
|
|
39
|
+
export interface RepeatingTimerSite {
|
|
40
|
+
readonly construct: RepeatingTimerConstruct;
|
|
41
|
+
/** The scheduling call itself, for reporting a position. */
|
|
42
|
+
readonly call: ts.CallExpression;
|
|
43
|
+
/**
|
|
44
|
+
* The callback body, or `null` when the argument could not be bound to a
|
|
45
|
+
* function in this file. Only a `setInterval` site can carry `null`.
|
|
46
|
+
*/
|
|
47
|
+
readonly callback: ts.Node | null;
|
|
48
|
+
/** 1-based line of the scheduling call. */
|
|
49
|
+
readonly line: number;
|
|
50
|
+
/** The nearest enclosing named function, or `<module scope>`. */
|
|
51
|
+
readonly scheduler: string;
|
|
52
|
+
}
|
|
53
|
+
export declare const MODULE_SCOPE = "<module scope>";
|
|
54
|
+
/** Parse with parent pointers — every predicate below walks upwards. */
|
|
55
|
+
export declare function parseScript(file: string, source: string): ts.SourceFile;
|
|
56
|
+
/** The trailing identifier of a callee: `globalThis.setInterval` → `setInterval`. */
|
|
57
|
+
export declare function calleeName(node: ts.CallExpression): string | null;
|
|
58
|
+
/** The receiver of a call, as its trailing identifier: `process.on` → `process`. */
|
|
59
|
+
export declare function receiverName(node: ts.CallExpression): string | null;
|
|
60
|
+
export declare function stringLiteralOf(node: ts.Node): string | null;
|
|
61
|
+
export type FunctionLike = ts.ArrowFunction | ts.FunctionExpression | ts.FunctionDeclaration;
|
|
62
|
+
export declare function isFunctionLike(node: ts.Node): node is FunctionLike;
|
|
63
|
+
/** File-local `function f(){}` / `const f = () => {}` bindings, by name. */
|
|
64
|
+
export declare function localFunctions(sf: ts.SourceFile): Map<string, FunctionLike>;
|
|
65
|
+
/** The nearest named function around `node`, for the ledger key. */
|
|
66
|
+
export declare function enclosingName(node: ts.Node): string | null;
|
|
67
|
+
/** True when the subtree contains a call to one of `names` (as identifier or tail). */
|
|
68
|
+
export declare function callsAnyOf(root: ts.Node, names: ReadonlySet<string>): boolean;
|
|
69
|
+
/**
|
|
70
|
+
* A `setTimeout` repeats only when its callback re-arms a timer itself, or calls
|
|
71
|
+
* back into the function that armed this one. That is the single shape a one-shot
|
|
72
|
+
* deadline cannot accidentally match.
|
|
73
|
+
*/
|
|
74
|
+
export declare function isSelfRescheduling(callback: ts.Node, armedBy: string | null): boolean;
|
|
75
|
+
/** Resolve a callback argument to a function body in this file, if we can read one. */
|
|
76
|
+
export declare function callbackOf(argument: ts.Node | undefined, bindings: ReadonlyMap<string, FunctionLike>): ts.Node | null;
|
|
77
|
+
/** Every repeating-timer entry point in one parsed file, in source order. */
|
|
78
|
+
export declare function findRepeatingTimerSites(sf: ts.SourceFile): RepeatingTimerSite[];
|
|
79
|
+
//# sourceMappingURL=repeating-timers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repeating-timers.d.ts","sourceRoot":"","sources":["../../src/lib/repeating-timers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B,iDAAiD;AACjD,MAAM,MAAM,uBAAuB,GAAG,aAAa,GAAG,YAAY,CAAC;AAEnE,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,SAAS,EAAE,uBAAuB,CAAC;IAC5C,4DAA4D;IAC5D,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,cAAc,CAAC;IACjC;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,EAAE,CAAC,IAAI,GAAG,IAAI,CAAC;IAClC,2CAA2C;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,YAAY,mBAAmB,CAAC;AAE7C,wEAAwE;AACxE,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,EAAE,CAAC,UAAU,CAEvE;AAED,qFAAqF;AACrF,wBAAgB,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC,cAAc,GAAG,MAAM,GAAG,IAAI,CAKjE;AAED,oFAAoF;AACpF,wBAAgB,YAAY,CAAC,IAAI,EAAE,EAAE,CAAC,cAAc,GAAG,MAAM,GAAG,IAAI,CAOnE;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,MAAM,GAAG,IAAI,CAU5D;AAED,MAAM,MAAM,YAAY,GAAG,EAAE,CAAC,aAAa,GAAG,EAAE,CAAC,kBAAkB,GAAG,EAAE,CAAC,mBAAmB,CAAC;AAE7F,wBAAgB,cAAc,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,IAAI,IAAI,YAAY,CAElE;AAED,4EAA4E;AAC5E,wBAAgB,cAAc,CAAC,EAAE,EAAE,EAAE,CAAC,UAAU,GAAG,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAiB3E;AAED,oEAAoE;AACpE,wBAAgB,aAAa,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,MAAM,GAAG,IAAI,CAuB1D;AAED,uFAAuF;AACvF,wBAAgB,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,OAAO,CAe7E;AAID;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAIrF;AAED,uFAAuF;AACvF,wBAAgB,UAAU,CACxB,QAAQ,EAAE,EAAE,CAAC,IAAI,GAAG,SAAS,EAC7B,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,YAAY,CAAC,GAC1C,EAAE,CAAC,IAAI,GAAG,IAAI,CAKhB;AAED,6EAA6E;AAC7E,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,EAAE,CAAC,UAAU,GAAG,kBAAkB,EAAE,CAkC/E"}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one recognizer for "this file starts a repeating execution of its own"
|
|
3
|
+
* (issue #128).
|
|
4
|
+
*
|
|
5
|
+
* Two checks ask that question and used to answer it separately.
|
|
6
|
+
* `check-entry-presence.ts` reads the callback and knows a repeating timer is
|
|
7
|
+
* not only a `setInterval`: a `setTimeout` whose callback re-arms a timer is the
|
|
8
|
+
* same entry point wearing a different constructor. `check-entry-scope.ts`
|
|
9
|
+
* grepped for `setInterval(` — so `search`'s reindex loop, which is exactly that
|
|
10
|
+
* `setTimeout`, was never in its population, and the `interval=4` it printed
|
|
11
|
+
* every run read as a settled count rather than as one spelling's worth of
|
|
12
|
+
* coverage.
|
|
13
|
+
*
|
|
14
|
+
* The fix is a shared recognizer rather than a second one. Lending only the last
|
|
15
|
+
* predicate (`isSelfRescheduling`) would have left the caller to re-implement the
|
|
16
|
+
* parse, the callback binding and the enclosing-function name — most of the
|
|
17
|
+
* recognizer — and those are precisely the parts that drift. So the whole shape
|
|
18
|
+
* lives here and both checks consume it; neither owns it.
|
|
19
|
+
*
|
|
20
|
+
* What this deliberately does **not** decide: what to do about a site it finds.
|
|
21
|
+
* `check-entry-presence` asks whether the callback decides presence,
|
|
22
|
+
* `check-entry-scope` whether the file opens a scope. The shapes are shared, the
|
|
23
|
+
* rules are not.
|
|
24
|
+
*
|
|
25
|
+
* ## Limits, and why they are the same for both callers
|
|
26
|
+
*
|
|
27
|
+
* - A one-shot `setTimeout` — an `AbortController` deadline, a sleep between
|
|
28
|
+
* retries — is not a repeating entry point and is not reported.
|
|
29
|
+
* - A callback that is an identifier bound in another file cannot be read, so
|
|
30
|
+
* a `setTimeout` reached that way is invisible. A `setInterval` is reported
|
|
31
|
+
* anyway, with a null callback: it repeats whatever its callback turns out to
|
|
32
|
+
* be, and what a check cannot see it must not vouch for.
|
|
33
|
+
* - A scheduler hidden behind an imported helper is invisible. The analysis is
|
|
34
|
+
* single-file and syntactic; if that shape arrives, this module has to grow.
|
|
35
|
+
*/
|
|
36
|
+
import ts from 'typescript';
|
|
37
|
+
export const MODULE_SCOPE = '<module scope>';
|
|
38
|
+
/** Parse with parent pointers — every predicate below walks upwards. */
|
|
39
|
+
export function parseScript(file, source) {
|
|
40
|
+
return ts.createSourceFile(file, source, ts.ScriptTarget.Latest, true);
|
|
41
|
+
}
|
|
42
|
+
/** The trailing identifier of a callee: `globalThis.setInterval` → `setInterval`. */
|
|
43
|
+
export function calleeName(node) {
|
|
44
|
+
const expression = node.expression;
|
|
45
|
+
if (ts.isIdentifier(expression))
|
|
46
|
+
return expression.text;
|
|
47
|
+
if (ts.isPropertyAccessExpression(expression))
|
|
48
|
+
return expression.name.text;
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
/** The receiver of a call, as its trailing identifier: `process.on` → `process`. */
|
|
52
|
+
export function receiverName(node) {
|
|
53
|
+
const expression = node.expression;
|
|
54
|
+
if (!ts.isPropertyAccessExpression(expression))
|
|
55
|
+
return null;
|
|
56
|
+
const receiver = expression.expression;
|
|
57
|
+
if (ts.isIdentifier(receiver))
|
|
58
|
+
return receiver.text;
|
|
59
|
+
if (ts.isPropertyAccessExpression(receiver))
|
|
60
|
+
return receiver.name.text;
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
export function stringLiteralOf(node) {
|
|
64
|
+
if (ts.isStringLiteralLike(node))
|
|
65
|
+
return node.text;
|
|
66
|
+
if (ts.isAsExpression(node) ||
|
|
67
|
+
ts.isNonNullExpression(node) ||
|
|
68
|
+
ts.isParenthesizedExpression(node)) {
|
|
69
|
+
return stringLiteralOf(node.expression);
|
|
70
|
+
}
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
export function isFunctionLike(node) {
|
|
74
|
+
return ts.isArrowFunction(node) || ts.isFunctionExpression(node) || ts.isFunctionDeclaration(node);
|
|
75
|
+
}
|
|
76
|
+
/** File-local `function f(){}` / `const f = () => {}` bindings, by name. */
|
|
77
|
+
export function localFunctions(sf) {
|
|
78
|
+
const bindings = new Map();
|
|
79
|
+
const visit = (node) => {
|
|
80
|
+
if (ts.isFunctionDeclaration(node) && node.name) {
|
|
81
|
+
bindings.set(node.name.text, node);
|
|
82
|
+
}
|
|
83
|
+
else if (ts.isVariableDeclaration(node) &&
|
|
84
|
+
ts.isIdentifier(node.name) &&
|
|
85
|
+
node.initializer &&
|
|
86
|
+
isFunctionLike(node.initializer)) {
|
|
87
|
+
bindings.set(node.name.text, node.initializer);
|
|
88
|
+
}
|
|
89
|
+
node.forEachChild(visit);
|
|
90
|
+
};
|
|
91
|
+
sf.forEachChild(visit);
|
|
92
|
+
return bindings;
|
|
93
|
+
}
|
|
94
|
+
/** The nearest named function around `node`, for the ledger key. */
|
|
95
|
+
export function enclosingName(node) {
|
|
96
|
+
for (let cursor = node.parent; cursor; cursor = cursor.parent) {
|
|
97
|
+
if (ts.isFunctionDeclaration(cursor) && cursor.name)
|
|
98
|
+
return cursor.name.text;
|
|
99
|
+
if (ts.isMethodDeclaration(cursor) && ts.isIdentifier(cursor.name))
|
|
100
|
+
return cursor.name.text;
|
|
101
|
+
// `{ plugin: async (app) => { … } }` — the shape every module's plugin body
|
|
102
|
+
// has, and the name a reader would use for it.
|
|
103
|
+
if (ts.isPropertyAssignment(cursor) &&
|
|
104
|
+
ts.isIdentifier(cursor.name) &&
|
|
105
|
+
isFunctionLike(cursor.initializer)) {
|
|
106
|
+
return cursor.name.text;
|
|
107
|
+
}
|
|
108
|
+
if (ts.isVariableDeclaration(cursor) &&
|
|
109
|
+
ts.isIdentifier(cursor.name) &&
|
|
110
|
+
cursor.initializer &&
|
|
111
|
+
isFunctionLike(cursor.initializer)) {
|
|
112
|
+
return cursor.name.text;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
/** True when the subtree contains a call to one of `names` (as identifier or tail). */
|
|
118
|
+
export function callsAnyOf(root, names) {
|
|
119
|
+
let found = false;
|
|
120
|
+
const visit = (node) => {
|
|
121
|
+
if (found)
|
|
122
|
+
return;
|
|
123
|
+
if (ts.isCallExpression(node)) {
|
|
124
|
+
const name = calleeName(node);
|
|
125
|
+
if (name !== null && names.has(name)) {
|
|
126
|
+
found = true;
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
node.forEachChild(visit);
|
|
131
|
+
};
|
|
132
|
+
root.forEachChild(visit);
|
|
133
|
+
return found;
|
|
134
|
+
}
|
|
135
|
+
const TIMER_CALLS = new Set(['setInterval', 'setTimeout']);
|
|
136
|
+
/**
|
|
137
|
+
* A `setTimeout` repeats only when its callback re-arms a timer itself, or calls
|
|
138
|
+
* back into the function that armed this one. That is the single shape a one-shot
|
|
139
|
+
* deadline cannot accidentally match.
|
|
140
|
+
*/
|
|
141
|
+
export function isSelfRescheduling(callback, armedBy) {
|
|
142
|
+
if (callsAnyOf(callback, TIMER_CALLS))
|
|
143
|
+
return true;
|
|
144
|
+
if (armedBy === null)
|
|
145
|
+
return false;
|
|
146
|
+
return callsAnyOf(callback, new Set([armedBy]));
|
|
147
|
+
}
|
|
148
|
+
/** Resolve a callback argument to a function body in this file, if we can read one. */
|
|
149
|
+
export function callbackOf(argument, bindings) {
|
|
150
|
+
if (argument === undefined)
|
|
151
|
+
return null;
|
|
152
|
+
if (isFunctionLike(argument))
|
|
153
|
+
return argument;
|
|
154
|
+
if (ts.isIdentifier(argument))
|
|
155
|
+
return bindings.get(argument.text) ?? null;
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
/** Every repeating-timer entry point in one parsed file, in source order. */
|
|
159
|
+
export function findRepeatingTimerSites(sf) {
|
|
160
|
+
const bindings = localFunctions(sf);
|
|
161
|
+
const sites = [];
|
|
162
|
+
const record = (call, construct, callback) => {
|
|
163
|
+
sites.push({
|
|
164
|
+
construct,
|
|
165
|
+
call,
|
|
166
|
+
callback,
|
|
167
|
+
line: sf.getLineAndCharacterOfPosition(call.getStart(sf)).line + 1,
|
|
168
|
+
scheduler: enclosingName(call) ?? MODULE_SCOPE,
|
|
169
|
+
});
|
|
170
|
+
};
|
|
171
|
+
const visit = (node) => {
|
|
172
|
+
if (ts.isCallExpression(node)) {
|
|
173
|
+
const name = calleeName(node);
|
|
174
|
+
if (name === 'setInterval' || name === 'setTimeout') {
|
|
175
|
+
const callback = callbackOf(node.arguments[0], bindings);
|
|
176
|
+
if (name === 'setInterval') {
|
|
177
|
+
record(node, 'setInterval', callback);
|
|
178
|
+
}
|
|
179
|
+
else if (callback !== null && isSelfRescheduling(callback, enclosingName(node))) {
|
|
180
|
+
record(node, 'setTimeout', callback);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
node.forEachChild(visit);
|
|
185
|
+
};
|
|
186
|
+
sf.forEachChild(visit);
|
|
187
|
+
return sites;
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=repeating-timers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repeating-timers.js","sourceRoot":"","sources":["../../src/lib/repeating-timers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,OAAO,EAAE,MAAM,YAAY,CAAC;AAoB5B,MAAM,CAAC,MAAM,YAAY,GAAG,gBAAgB,CAAC;AAE7C,wEAAwE;AACxE,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,MAAc;IACtD,OAAO,EAAE,CAAC,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AACzE,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,UAAU,CAAC,IAAuB;IAChD,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC;IACnC,IAAI,EAAE,CAAC,YAAY,CAAC,UAAU,CAAC;QAAE,OAAO,UAAU,CAAC,IAAI,CAAC;IACxD,IAAI,EAAE,CAAC,0BAA0B,CAAC,UAAU,CAAC;QAAE,OAAO,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC;IAC3E,OAAO,IAAI,CAAC;AACd,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,YAAY,CAAC,IAAuB;IAClD,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC;IACnC,IAAI,CAAC,EAAE,CAAC,0BAA0B,CAAC,UAAU,CAAC;QAAE,OAAO,IAAI,CAAC;IAC5D,MAAM,QAAQ,GAAG,UAAU,CAAC,UAAU,CAAC;IACvC,IAAI,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC;QAAE,OAAO,QAAQ,CAAC,IAAI,CAAC;IACpD,IAAI,EAAE,CAAC,0BAA0B,CAAC,QAAQ,CAAC;QAAE,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;IACvE,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAAa;IAC3C,IAAI,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,IAAI,CAAC;IACnD,IACE,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC;QACvB,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC;QAC5B,EAAE,CAAC,yBAAyB,CAAC,IAAI,CAAC,EAClC,CAAC;QACD,OAAO,eAAe,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC1C,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAID,MAAM,UAAU,cAAc,CAAC,IAAa;IAC1C,OAAO,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC;AACrG,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,cAAc,CAAC,EAAiB;IAC9C,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAwB,CAAC;IACjD,MAAM,KAAK,GAAG,CAAC,IAAa,EAAQ,EAAE;QACpC,IAAI,EAAE,CAAC,qBAAqB,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YAChD,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACrC,CAAC;aAAM,IACL,EAAE,CAAC,qBAAqB,CAAC,IAAI,CAAC;YAC9B,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;YAC1B,IAAI,CAAC,WAAW;YAChB,cAAc,CAAC,IAAI,CAAC,WAAW,CAAC,EAChC,CAAC;YACD,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QACjD,CAAC;QACD,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IAC3B,CAAC,CAAC;IACF,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IACvB,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,aAAa,CAAC,IAAa;IACzC,KAAK,IAAI,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC;QAC9D,IAAI,EAAE,CAAC,qBAAqB,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI;YAAE,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;QAC7E,IAAI,EAAE,CAAC,mBAAmB,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC;YAAE,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;QAC5F,4EAA4E;QAC5E,+CAA+C;QAC/C,IACE,EAAE,CAAC,oBAAoB,CAAC,MAAM,CAAC;YAC/B,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC;YAC5B,cAAc,CAAC,MAAM,CAAC,WAAW,CAAC,EAClC,CAAC;YACD,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;QAC1B,CAAC;QACD,IACE,EAAE,CAAC,qBAAqB,CAAC,MAAM,CAAC;YAChC,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC;YAC5B,MAAM,CAAC,WAAW;YAClB,cAAc,CAAC,MAAM,CAAC,WAAW,CAAC,EAClC,CAAC;YACD,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;QAC1B,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,UAAU,CAAC,IAAa,EAAE,KAA0B;IAClE,IAAI,KAAK,GAAG,KAAK,CAAC;IAClB,MAAM,KAAK,GAAG,CAAC,IAAa,EAAQ,EAAE;QACpC,IAAI,KAAK;YAAE,OAAO;QAClB,IAAI,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;YAC9B,IAAI,IAAI,KAAK,IAAI,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBACrC,KAAK,GAAG,IAAI,CAAC;gBACb,OAAO;YACT,CAAC;QACH,CAAC;QACD,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IAC3B,CAAC,CAAC;IACF,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IACzB,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC,CAAC;AAE3D;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,QAAiB,EAAE,OAAsB;IAC1E,IAAI,UAAU,CAAC,QAAQ,EAAE,WAAW,CAAC;QAAE,OAAO,IAAI,CAAC;IACnD,IAAI,OAAO,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IACnC,OAAO,UAAU,CAAC,QAAQ,EAAE,IAAI,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AAClD,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,UAAU,CACxB,QAA6B,EAC7B,QAA2C;IAE3C,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACxC,IAAI,cAAc,CAAC,QAAQ,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC9C,IAAI,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC;QAAE,OAAO,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;IAC1E,OAAO,IAAI,CAAC;AACd,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,uBAAuB,CAAC,EAAiB;IACvD,MAAM,QAAQ,GAAG,cAAc,CAAC,EAAE,CAAC,CAAC;IACpC,MAAM,KAAK,GAAyB,EAAE,CAAC;IAEvC,MAAM,MAAM,GAAG,CACb,IAAuB,EACvB,SAAkC,EAClC,QAAwB,EAClB,EAAE;QACR,KAAK,CAAC,IAAI,CAAC;YACT,SAAS;YACT,IAAI;YACJ,QAAQ;YACR,IAAI,EAAE,EAAE,CAAC,6BAA6B,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC;YAClE,SAAS,EAAE,aAAa,CAAC,IAAI,CAAC,IAAI,YAAY;SAC/C,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,MAAM,KAAK,GAAG,CAAC,IAAa,EAAQ,EAAE;QACpC,IAAI,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;YAC9B,IAAI,IAAI,KAAK,aAAa,IAAI,IAAI,KAAK,YAAY,EAAE,CAAC;gBACpD,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;gBACzD,IAAI,IAAI,KAAK,aAAa,EAAE,CAAC;oBAC3B,MAAM,CAAC,IAAI,EAAE,aAAa,EAAE,QAAQ,CAAC,CAAC;gBACxC,CAAC;qBAAM,IAAI,QAAQ,KAAK,IAAI,IAAI,kBAAkB,CAAC,QAAQ,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;oBAClF,MAAM,CAAC,IAAI,EAAE,YAAY,EAAE,QAAQ,CAAC,CAAC;gBACvC,CAAC;YACH,CAAC;QACH,CAAC;QACD,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IAC3B,CAAC,CAAC;IACF,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IACvB,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The tree's one comment stripper (issue #241).
|
|
3
|
+
*
|
|
4
|
+
* ## Why this is not two regexes
|
|
5
|
+
*
|
|
6
|
+
* "Remove the comments before matching" was written three times in this
|
|
7
|
+
* repository, independently, and each copy was a pair of regexes — one for
|
|
8
|
+
* `/* … *\/`, one for `//` — whose **order** decided whether it was correct.
|
|
9
|
+
* There is no correct order. Run the block pass first and a `//` line ending in
|
|
10
|
+
* a route glob opens a block comment: `harness-parity.test.ts` did exactly that
|
|
11
|
+
* over `test/helpers/test-server.ts`, whose line 1544 ends
|
|
12
|
+
* `… under /api/v1/admin/assets/` plus a star, and the pass ran on to the next
|
|
13
|
+
* real terminator — **1135 of 2767 lines, 41% of the file**. Every
|
|
14
|
+
* `expect(harnessCode).not.toContain(…)` inside that window was green because
|
|
15
|
+
* the text was gone, `runBootHooks(`, `errorEnvelope` and
|
|
16
|
+
* `resolvePreferredLanguage` among them. Run the line pass first and the
|
|
17
|
+
* symmetric hole opens: a `//` inside a block comment truncates it. And in
|
|
18
|
+
* either order a string literal holding a comment token — `'/*'` in
|
|
19
|
+
* `assets_library`'s wildcard-MIME test, `'image/*, *\/*;q=0.5'` in
|
|
20
|
+
* `pim_ergonode`'s Accept header — opens or closes a comment that is not there.
|
|
21
|
+
*
|
|
22
|
+
* So this asks the parser instead. `ts` is already a backend dependency and
|
|
23
|
+
* already the input to nineteen checks; a scanner that knows what a string
|
|
24
|
+
* literal is has no order to get wrong.
|
|
25
|
+
*
|
|
26
|
+
* ## Blanked, not deleted
|
|
27
|
+
*
|
|
28
|
+
* Every comment character becomes a space and every newline survives, so a line
|
|
29
|
+
* number in the result is a line number in the source. A stripper that deletes
|
|
30
|
+
* shifts every finding below the first comment onto a line the reader cannot
|
|
31
|
+
* open, and the three copies this replaces all did.
|
|
32
|
+
*/
|
|
33
|
+
export declare function codeOnly(source: string, fileName?: string): string;
|
|
34
|
+
//# sourceMappingURL=source-text.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-text.d.ts","sourceRoot":"","sources":["../../src/lib/source-text.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,SAAc,GAAG,MAAM,CAiBvE"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
/**
|
|
3
|
+
* The tree's one comment stripper (issue #241).
|
|
4
|
+
*
|
|
5
|
+
* ## Why this is not two regexes
|
|
6
|
+
*
|
|
7
|
+
* "Remove the comments before matching" was written three times in this
|
|
8
|
+
* repository, independently, and each copy was a pair of regexes — one for
|
|
9
|
+
* `/* … *\/`, one for `//` — whose **order** decided whether it was correct.
|
|
10
|
+
* There is no correct order. Run the block pass first and a `//` line ending in
|
|
11
|
+
* a route glob opens a block comment: `harness-parity.test.ts` did exactly that
|
|
12
|
+
* over `test/helpers/test-server.ts`, whose line 1544 ends
|
|
13
|
+
* `… under /api/v1/admin/assets/` plus a star, and the pass ran on to the next
|
|
14
|
+
* real terminator — **1135 of 2767 lines, 41% of the file**. Every
|
|
15
|
+
* `expect(harnessCode).not.toContain(…)` inside that window was green because
|
|
16
|
+
* the text was gone, `runBootHooks(`, `errorEnvelope` and
|
|
17
|
+
* `resolvePreferredLanguage` among them. Run the line pass first and the
|
|
18
|
+
* symmetric hole opens: a `//` inside a block comment truncates it. And in
|
|
19
|
+
* either order a string literal holding a comment token — `'/*'` in
|
|
20
|
+
* `assets_library`'s wildcard-MIME test, `'image/*, *\/*;q=0.5'` in
|
|
21
|
+
* `pim_ergonode`'s Accept header — opens or closes a comment that is not there.
|
|
22
|
+
*
|
|
23
|
+
* So this asks the parser instead. `ts` is already a backend dependency and
|
|
24
|
+
* already the input to nineteen checks; a scanner that knows what a string
|
|
25
|
+
* literal is has no order to get wrong.
|
|
26
|
+
*
|
|
27
|
+
* ## Blanked, not deleted
|
|
28
|
+
*
|
|
29
|
+
* Every comment character becomes a space and every newline survives, so a line
|
|
30
|
+
* number in the result is a line number in the source. A stripper that deletes
|
|
31
|
+
* shifts every finding below the first comment onto a line the reader cannot
|
|
32
|
+
* open, and the three copies this replaces all did.
|
|
33
|
+
*/
|
|
34
|
+
export function codeOnly(source, fileName = 'source.ts') {
|
|
35
|
+
const scriptKind = fileName.endsWith('.tsx') ? ts.ScriptKind.TSX : ts.ScriptKind.TS;
|
|
36
|
+
const sourceFile = ts.createSourceFile(fileName, source, ts.ScriptTarget.ES2022, true, scriptKind);
|
|
37
|
+
const characters = [...source];
|
|
38
|
+
for (const range of commentRanges(sourceFile, source)) {
|
|
39
|
+
for (let index = range.pos; index < range.end; index += 1) {
|
|
40
|
+
if (characters[index] !== '\n')
|
|
41
|
+
characters[index] = ' ';
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return characters.join('');
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Every comment span the parser saw, sorted.
|
|
48
|
+
*
|
|
49
|
+
* Trivia hangs off token positions rather than off nodes of its own, so the
|
|
50
|
+
* walk asks at both ends of every node: a comment is leading trivia for the
|
|
51
|
+
* token after it and trailing trivia for the token before it, and the file's
|
|
52
|
+
* last comment is leading trivia of the end-of-file token. Ranges repeat under
|
|
53
|
+
* that walk, which is what the `seen` set is for.
|
|
54
|
+
*/
|
|
55
|
+
function commentRanges(sourceFile, source) {
|
|
56
|
+
const found = [];
|
|
57
|
+
const seen = new Set();
|
|
58
|
+
const collect = (position) => {
|
|
59
|
+
const ranges = [
|
|
60
|
+
...(ts.getLeadingCommentRanges(source, position) ?? []),
|
|
61
|
+
...(ts.getTrailingCommentRanges(source, position) ?? []),
|
|
62
|
+
];
|
|
63
|
+
for (const range of ranges) {
|
|
64
|
+
const key = `${range.pos}:${range.end}`;
|
|
65
|
+
if (seen.has(key))
|
|
66
|
+
continue;
|
|
67
|
+
seen.add(key);
|
|
68
|
+
found.push(range);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
const visit = (node) => {
|
|
72
|
+
collect(node.getFullStart());
|
|
73
|
+
collect(node.getEnd());
|
|
74
|
+
for (const child of node.getChildren(sourceFile))
|
|
75
|
+
visit(child);
|
|
76
|
+
};
|
|
77
|
+
visit(sourceFile);
|
|
78
|
+
return found.sort((a, b) => a.pos - b.pos);
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=source-text.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-text.js","sourceRoot":"","sources":["../../src/lib/source-text.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,UAAU,QAAQ,CAAC,MAAc,EAAE,QAAQ,GAAG,WAAW;IAC7D,MAAM,UAAU,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC;IACpF,MAAM,UAAU,GAAG,EAAE,CAAC,gBAAgB,CACpC,QAAQ,EACR,MAAM,EACN,EAAE,CAAC,YAAY,CAAC,MAAM,EACtB,IAAI,EACJ,UAAU,CACX,CAAC;IAEF,MAAM,UAAU,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC;IAC/B,KAAK,MAAM,KAAK,IAAI,aAAa,CAAC,UAAU,EAAE,MAAM,CAAC,EAAE,CAAC;QACtD,KAAK,IAAI,KAAK,GAAG,KAAK,CAAC,GAAG,EAAE,KAAK,GAAG,KAAK,CAAC,GAAG,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;YAC1D,IAAI,UAAU,CAAC,KAAK,CAAC,KAAK,IAAI;gBAAE,UAAU,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC;QAC1D,CAAC;IACH,CAAC;IACD,OAAO,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAC7B,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,aAAa,CAAC,UAAyB,EAAE,MAAc;IAC9D,MAAM,KAAK,GAAsB,EAAE,CAAC;IACpC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAE/B,MAAM,OAAO,GAAG,CAAC,QAAgB,EAAQ,EAAE;QACzC,MAAM,MAAM,GAAG;YACb,GAAG,CAAC,EAAE,CAAC,uBAAuB,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC;YACvD,GAAG,CAAC,EAAE,CAAC,wBAAwB,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC;SACzD,CAAC;QACF,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,MAAM,GAAG,GAAG,GAAG,KAAK,CAAC,GAAG,IAAI,KAAK,CAAC,GAAG,EAAE,CAAC;YACxC,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,SAAS;YAC5B,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACd,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpB,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,KAAK,GAAG,CAAC,IAAa,EAAQ,EAAE;QACpC,OAAO,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC;QAC7B,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;QACvB,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC;YAAE,KAAK,CAAC,KAAK,CAAC,CAAC;IACjE,CAAC,CAAC;IACF,KAAK,CAAC,UAAU,CAAC,CAAC;IAElB,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;AAC7C,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** The shape a specifier was written in. See the header for why each is listed. */
|
|
2
|
+
export type SpecifierKind = 'value-import' | 'type-only-import' | 'mixed-type-specifier' | 're-export' | 'side-effect-import' | 'dynamic-import' | 'require-call' | 'import-type-node';
|
|
3
|
+
/** One specifier as it was written, with everything a boundary rule needs of it. */
|
|
4
|
+
export interface NamedSpecifier {
|
|
5
|
+
/** The specifier exactly as written, extension and all. */
|
|
6
|
+
readonly text: string;
|
|
7
|
+
readonly kind: SpecifierKind;
|
|
8
|
+
/** What the import takes: a reviewer's first question is shape or behaviour. */
|
|
9
|
+
readonly bindings: readonly string[];
|
|
10
|
+
readonly line: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Every module specifier `source` names, in source order.
|
|
14
|
+
*
|
|
15
|
+
* `file` is used only for the source-file name the compiler API wants and for
|
|
16
|
+
* line numbers; nothing here touches disk, so a caller may pass a path that does
|
|
17
|
+
* not exist — which is what lets both checks' fixtures enter at the top.
|
|
18
|
+
*/
|
|
19
|
+
export declare function namedSpecifiers(source: string, file: string): NamedSpecifier[];
|
|
20
|
+
//# sourceMappingURL=specifiers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"specifiers.d.ts","sourceRoot":"","sources":["../../src/lib/specifiers.ts"],"names":[],"mappings":"AAsCA,mFAAmF;AACnF,MAAM,MAAM,aAAa,GACrB,cAAc,GACd,kBAAkB,GAClB,sBAAsB,GACtB,WAAW,GACX,oBAAoB,GACpB,gBAAgB,GAChB,cAAc,GACd,kBAAkB,CAAC;AAEvB,oFAAoF;AACpF,MAAM,WAAW,cAAc;IAC7B,2DAA2D;IAC3D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,gFAAgF;IAChF,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AA2CD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,cAAc,EAAE,CAqD9E"}
|