@stacksjs/stx 0.2.154 → 0.2.156
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/dist/a11y.d.ts +87 -46
- package/dist/animation.d.ts +96 -42
- package/dist/appearance-bootstrap.d.ts +49 -15
- package/dist/asset-roots.d.ts +32 -0
- package/dist/asset-roots.js +7 -0
- package/dist/assets.d.ts +9 -7
- package/dist/async-components.d.ts +78 -56
- package/dist/auth.d.ts +3 -0
- package/dist/build-externalize.d.ts +40 -0
- package/dist/build-externalize.js +63 -0
- package/dist/build-id.d.ts +22 -22
- package/dist/build-message.d.ts +73 -2
- package/dist/build-message.js +47 -8
- package/dist/builtins/attrs.d.ts +14 -0
- package/dist/builtins/drawer.d.ts +22 -0
- package/dist/builtins/icon.d.ts +16 -2
- package/dist/builtins/index.d.ts +5 -15
- package/dist/builtins/modal.d.ts +24 -0
- package/dist/builtins/safe-image.d.ts +42 -0
- package/dist/builtins/stx-image.d.ts +27 -0
- package/dist/builtins/stx-link.d.ts +9 -0
- package/dist/builtins/stx-loading-indicator.d.ts +11 -0
- package/dist/builtins/suspense.d.ts +28 -0
- package/dist/builtins/toast.d.ts +17 -0
- package/dist/builtins/transition-group.d.ts +25 -0
- package/dist/caching.d.ts +12 -15
- package/dist/cli.js +1798 -908
- package/dist/client/directive.d.ts +7 -0
- package/dist/client/index.d.ts +0 -1
- package/dist/client-script-bundler.d.ts +63 -5
- package/dist/client-script-bundler.js +30 -2
- package/dist/client-script.d.ts +102 -47
- package/dist/client-script.js +15 -3
- package/dist/codemod.d.ts +56 -0
- package/dist/codemod.js +192 -0
- package/dist/color-mode-boot.d.ts +83 -65
- package/dist/component-client-factories.d.ts +1 -6
- package/dist/component-processing.d.ts +58 -49
- package/dist/component-processing.js +1 -1
- package/dist/component-registry.d.ts +94 -31
- package/dist/component-renderer.d.ts +18 -0
- package/dist/composable-loader.d.ts +24 -0
- package/dist/composable-loader.js +12 -9
- package/dist/composables/index.d.ts +7 -0
- package/dist/composables/index.js +1 -0
- package/dist/composables/use-fetch.js +30 -2
- package/dist/composables/use-form.d.ts +91 -0
- package/dist/composables/use-form.js +112 -0
- package/dist/composables.d.ts +64 -60
- package/dist/computed.d.ts +86 -92
- package/dist/conditionals.d.ts +24 -8
- package/dist/conditionals.js +16 -0
- package/dist/config.d.ts +34 -21
- package/dist/config.js +35 -14
- package/dist/craft.js +1476 -768
- package/dist/crosswind-config.d.ts +58 -0
- package/dist/crosswind-config.js +40 -0
- package/dist/csp.d.ts +107 -41
- package/dist/defer.d.ts +24 -0
- package/dist/dev-server/crosswind.d.ts +35 -26
- package/dist/dev-server/crosswind.js +2 -18
- package/dist/dev-server/serve-app.js +4 -3
- package/dist/dev-server/terminal-colors.d.ts +32 -32
- package/dist/directive-api.d.ts +86 -43
- package/dist/document-shell.d.ts +54 -41
- package/dist/dynamic-components.d.ts +9 -0
- package/dist/error-boundaries.d.ts +47 -41
- package/dist/error-handling.d.ts +1 -1
- package/dist/errors/codes.d.ts +51 -43
- package/dist/errors/formatter.d.ts +58 -38
- package/dist/errors/index.d.ts +5 -61
- package/dist/errors/logger.d.ts +114 -40
- package/dist/errors/sanitizer.d.ts +53 -23
- package/dist/errors/types.d.ts +44 -31
- package/dist/events.d.ts +86 -25
- package/dist/expressions.d.ts +50 -42
- package/dist/forms-validation.d.ts +138 -123
- package/dist/forms-validation.js +1 -90
- package/dist/forms.d.ts +120 -66
- package/dist/forms.js +31 -11
- package/dist/fragment-container.d.ts +57 -0
- package/dist/fragment-container.js +109 -0
- package/dist/framework-composables.d.ts +32 -0
- package/dist/head.d.ts +156 -114
- package/dist/heatmap.d.ts +152 -72
- package/dist/html-masking.d.ts +40 -34
- package/dist/hydration-runtime.d.ts +20 -24
- package/dist/i18n.d.ts +93 -32
- package/dist/image-optimization/auto-sibling.d.ts +32 -4
- package/dist/includes.d.ts +69 -7
- package/dist/index.d.ts +66 -0
- package/dist/index.js +23 -0
- package/dist/internal-markdown.d.ts +12 -8
- package/dist/island-chunking.d.ts +30 -13
- package/dist/keep-alive.d.ts +72 -52
- package/dist/layout-types.d.ts +45 -0
- package/dist/layout-types.js +56 -0
- package/dist/lazy-module.d.ts +10 -0
- package/dist/loops.d.ts +66 -0
- package/dist/loops.js +47 -6
- package/dist/markdown.d.ts +0 -1
- package/dist/media/image/component.d.ts +25 -9
- package/dist/media/image/directive.d.ts +15 -1
- package/dist/media/image/placeholder.d.ts +20 -9
- package/dist/media/image/processor/cache.d.ts +40 -18
- package/dist/media/image/processor/index.d.ts +1 -10
- package/dist/media/image/processor/optimizer.d.ts +12 -1
- package/dist/media/image/processor/responsive.d.ts +16 -6
- package/dist/media/image/srcset.d.ts +47 -50
- package/dist/media/shared/cache.d.ts +51 -31
- package/dist/media/shared/hash.d.ts +7 -0
- package/dist/media/types.d.ts +1235 -754
- package/dist/media/video/directive.d.ts +15 -1
- package/dist/media/video/index.d.ts +39 -17
- package/dist/media/video/processor/cache.d.ts +42 -19
- package/dist/media/video/processor/index.d.ts +1 -18
- package/dist/media/video/processor/streaming.d.ts +17 -10
- package/dist/media/video/processor/thumbnail.d.ts +21 -15
- package/dist/media/video/processor/transcoder.d.ts +11 -1
- package/dist/method-spoofing.d.ts +7 -0
- package/dist/middleware.d.ts +60 -0
- package/dist/owned-routes.d.ts +42 -0
- package/dist/owned-routes.js +26 -0
- package/dist/page-meta.d.ts +38 -0
- package/dist/page-meta.js +16 -0
- package/dist/parser/directive-parser.d.ts +57 -45
- package/dist/parser/expression-parser.d.ts +33 -35
- package/dist/parser/index.d.ts +4 -39
- package/dist/parser/tokenizer.d.ts +61 -49
- package/dist/partial-hydration.d.ts +82 -50
- package/dist/performance-utils.d.ts +217 -124
- package/dist/placeholder.d.ts +18 -9
- package/dist/plugin-system.d.ts +264 -115
- package/dist/process.d.ts +11 -6
- package/dist/process.js +21 -4
- package/dist/production-builder.js +7 -0
- package/dist/prop-sentinels.d.ts +11 -11
- package/dist/public-env.d.ts +14 -0
- package/dist/pwa/directives.d.ts +12 -3
- package/dist/pwa/inject.d.ts +10 -0
- package/dist/pwa/precache.d.ts +22 -16
- package/dist/pwa/service-worker.d.ts +12 -0
- package/dist/pwa.js +1318 -610
- package/dist/reactive-bindings.d.ts +34 -11
- package/dist/reactive.d.ts +58 -30
- package/dist/reactive.js +56 -31
- package/dist/render.js +1 -0
- package/dist/route-middleware.d.ts +2 -35
- package/dist/route-types.d.ts +134 -0
- package/dist/route-types.js +24 -0
- package/dist/router.d.ts +5 -7
- package/dist/routes.d.ts +1 -3
- package/dist/runtime-globals.d.ts +147 -24
- package/dist/runtime-globals.js +16 -1
- package/dist/runtime-injection.d.ts +8 -0
- package/dist/runtime-injection.js +33 -24
- package/dist/script-emit.d.ts +30 -0
- package/dist/script-json.d.ts +36 -0
- package/dist/script-json.js +6 -0
- package/dist/script-validation.d.ts +21 -4
- package/dist/seo.d.ts +118 -47
- package/dist/server-bindings.d.ts +21 -0
- package/dist/sfc-template.d.ts +8 -8
- package/dist/signal-processing.d.ts +35 -15
- package/dist/signals-api.d.ts +117 -58
- package/dist/signals-api.js +7 -4
- package/dist/signals.d.ts +24 -17
- package/dist/signals.js +765 -60
- package/dist/site-builder/build.js +8 -0
- package/dist/site-url.d.ts +46 -0
- package/dist/site-url.js +70 -0
- package/dist/slots.d.ts +86 -20
- package/dist/source-signature.d.ts +45 -0
- package/dist/source-signature.js +20 -0
- package/dist/ssg.d.ts +285 -142
- package/dist/ssg.js +1268 -556
- package/dist/state-dir.d.ts +4 -4
- package/dist/state-management.d.ts +0 -12
- package/dist/state-management.js +8 -25
- package/dist/store-imports.d.ts +23 -0
- package/dist/store-imports.js +3 -0
- package/dist/store-loader.d.ts +10 -0
- package/dist/store-loader.js +10 -9
- package/dist/story/analytics.d.ts +138 -60
- package/dist/story/bookmarks.d.ts +87 -34
- package/dist/story/cli.d.ts +123 -69
- package/dist/story/collect/scanner.d.ts +4 -0
- package/dist/story/collect/tree.d.ts +8 -1
- package/dist/story/commands/build.d.ts +11 -6
- package/dist/story/commands/dev.d.ts +15 -8
- package/dist/story/commands/preview.d.ts +13 -7
- package/dist/story/config-watcher.d.ts +20 -13
- package/dist/story/config.d.ts +11 -8
- package/dist/story/context.d.ts +14 -8
- package/dist/story/hmr.d.ts +24 -26
- package/dist/story/hot-swap.d.ts +34 -23
- package/dist/story/keyboard-shortcuts.d.ts +23 -18
- package/dist/story/output.d.ts +46 -44
- package/dist/story/performance.d.ts +107 -45
- package/dist/story/presets.d.ts +107 -41
- package/dist/story/renderer.d.ts +41 -27
- package/dist/story/search.d.ts +23 -12
- package/dist/story/server.d.ts +21 -11
- package/dist/story/setup.d.ts +23 -15
- package/dist/story/testing.d.ts +65 -40
- package/dist/story/types.d.ts +262 -160
- package/dist/streaming.d.ts +119 -57
- package/dist/stx-virtual-ts.d.ts +165 -0
- package/dist/stx-virtual-ts.js +372 -0
- package/dist/stx.d.ts +78 -1
- package/dist/style-scoping.d.ts +17 -4
- package/dist/suspense.d.ts +67 -52
- package/dist/teleport.d.ts +22 -0
- package/dist/template-tag-balance.d.ts +22 -6
- package/dist/transitions.d.ts +66 -55
- package/dist/typecheck.d.ts +77 -0
- package/dist/typecheck.js +156 -0
- package/dist/types/component-types.d.ts +187 -122
- package/dist/types/config-types.d.ts +758 -297
- package/dist/types/context-types.d.ts +46 -24
- package/dist/types/csp-types.d.ts +70 -66
- package/dist/types/directive-types.d.ts +211 -155
- package/dist/types/index.d.ts +6 -109
- package/dist/types/pwa-types.d.ts +273 -148
- package/dist/types.d.ts +0 -1
- package/dist/typescript-templates.d.ts +11 -0
- package/dist/unresolved-identifiers.d.ts +24 -22
- package/dist/unresolved-identifiers.js +2 -0
- package/dist/utils.d.ts +24 -21
- package/dist/validator.d.ts +28 -26
- package/dist/variable-extractor.d.ts +48 -13
- package/dist/variable-extractor.js +27 -4
- package/dist/view-composers.d.ts +80 -81
- package/dist/virtual-scrolling.d.ts +101 -81
- package/dist/vue-template.d.ts +23 -0
- package/dist/web-components/css-scoping.d.ts +21 -11
- package/dist/web-components.d.ts +81 -58
- package/dist/x-element.d.ts +0 -1
- package/package.json +5 -5
- package/stx.d.ts +78 -1
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* STX Router Directive
|
|
3
|
+
*
|
|
4
|
+
* Usage in templates:
|
|
5
|
+
* @stxRouter - Inject the SPA router script
|
|
6
|
+
* @stxRouter('main') - Specify custom container selector
|
|
7
|
+
*/
|
|
1
8
|
import type { CustomDirective } from '../types';
|
|
2
9
|
/**
|
|
3
10
|
* Handler for @stxRouter directive
|
package/dist/client/index.d.ts
CHANGED
|
@@ -1,4 +1,62 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Client Script Bundler
|
|
3
|
+
*
|
|
4
|
+
* Uses Bun.build to resolve and bundle imports in `<script client>` blocks.
|
|
5
|
+
* Only activates when real user imports are detected (local files, npm packages).
|
|
6
|
+
* stx runtime, @stores, and @composables are marked as external — handled
|
|
7
|
+
* by existing auto-import and store-import transforms.
|
|
8
|
+
*
|
|
9
|
+
* @module client-script-bundler
|
|
10
|
+
*/
|
|
11
|
+
import type { BuildFailureDetail } from './build-message';
|
|
12
|
+
export declare function shouldLogBundlerDiagnostics(environment?: {
|
|
13
|
+
STX_DEBUG?: string;
|
|
14
|
+
}): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Client-script bundle failures recorded during the current process.
|
|
17
|
+
*
|
|
18
|
+
* When a `<script client>` block fails to bundle, the bundler falls back to
|
|
19
|
+
* shipping the ORIGINAL, unbundled source so the page still renders. That is a
|
|
20
|
+
* reasonable dev-server behaviour and a terrible build behaviour: imports never
|
|
21
|
+
* resolve, bindings silently do nothing, and `stx build` still exited 0, so CI
|
|
22
|
+
* published a broken page as a success.
|
|
23
|
+
*
|
|
24
|
+
* Nothing was counting these. `ssg.failedCount` only ever saw render-time throws,
|
|
25
|
+
* never a bundle failure, because the fallback swallowed it and returned a
|
|
26
|
+
* string. The registry gives the build a way to ask, without the bundler having
|
|
27
|
+
* to know whether it is running under a dev server or a build. See #1884.
|
|
28
|
+
*/
|
|
29
|
+
export interface BundleFailure {
|
|
30
|
+
filePath: string;
|
|
31
|
+
message: string;
|
|
32
|
+
/**
|
|
33
|
+
* The same failure with its position intact, for a dev-server overlay to
|
|
34
|
+
* draw a code frame from. Absent only when the thrower gave no position.
|
|
35
|
+
*/
|
|
36
|
+
details?: BuildFailureDetail[];
|
|
37
|
+
}
|
|
38
|
+
/** Record a bundle failure. Called from the bundler's fallback path. */
|
|
39
|
+
export declare function recordBundleFailure(filePath: string, message: string, details?: BuildFailureDetail[]): void;
|
|
40
|
+
/** Every bundle failure since the last reset. */
|
|
41
|
+
export declare function getBundleFailures(): BundleFailure[];
|
|
42
|
+
/** Drop recorded failures — call at the start of a build, and in tests. */
|
|
43
|
+
export declare function clearBundleFailures(): void;
|
|
44
|
+
/** What to do when a client script will not bundle. */
|
|
45
|
+
export type BundlerFallbackMode = 'warn' | 'error';
|
|
46
|
+
/**
|
|
47
|
+
* Override the configured fallback mode, for tests and embedders.
|
|
48
|
+
*
|
|
49
|
+
* Pass `undefined` to go back to reading `strict.bundlerFallback` from config.
|
|
50
|
+
*/
|
|
51
|
+
export declare function setBundlerFallbackMode(mode: BundlerFallbackMode | undefined): void;
|
|
52
|
+
/**
|
|
53
|
+
* The effective fallback mode.
|
|
54
|
+
*
|
|
55
|
+
* Read lazily rather than captured at import time: config is loaded after this
|
|
56
|
+
* module is first imported, so a value read at import time is always the
|
|
57
|
+
* default.
|
|
58
|
+
*/
|
|
59
|
+
export declare function resolveBundlerFallback(): BundlerFallbackMode;
|
|
2
60
|
/**
|
|
3
61
|
* Check if a script has user imports that need Bun.build bundling.
|
|
4
62
|
*
|
|
@@ -24,7 +82,7 @@ export declare function hasUserImports(code: string): boolean;
|
|
|
24
82
|
*/
|
|
25
83
|
export declare function queueClientBundleBuild<T>(task: () => Promise<T>): Promise<T>;
|
|
26
84
|
export declare function bundleClientScript(code: string, filePath: string, options?: {
|
|
27
|
-
projectRoot?: string
|
|
28
|
-
minify?: boolean
|
|
29
|
-
cacheDir?: string
|
|
30
|
-
|
|
85
|
+
projectRoot?: string;
|
|
86
|
+
minify?: boolean;
|
|
87
|
+
cacheDir?: string;
|
|
88
|
+
}): Promise<string>;
|
|
@@ -2,7 +2,8 @@ import path from "node:path";
|
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import { getPublicEnvDefine } from "./public-env.js";
|
|
4
4
|
import { stateDir } from "./state-dir.js";
|
|
5
|
-
import { formatBuildFailure } from "./build-message.js";
|
|
5
|
+
import { describeBuildFailure, formatBuildFailure } from "./build-message.js";
|
|
6
|
+
import { config } from "./config.js";
|
|
6
7
|
const BUNDLE_CACHE_VERSION = 5, BUNDLE_CACHE_METADATA_VERSION = 1;
|
|
7
8
|
export function shouldLogBundlerDiagnostics(environment = process.env) {
|
|
8
9
|
return environment.STX_DEBUG === "true" || environment.STX_DEBUG === "1";
|
|
@@ -11,6 +12,29 @@ function logBundlerDiagnostic(...message) {
|
|
|
11
12
|
if (shouldLogBundlerDiagnostics())
|
|
12
13
|
console.log("[stx:bundler]", ...message);
|
|
13
14
|
}
|
|
15
|
+
const _bundleFailures = [];
|
|
16
|
+
export function recordBundleFailure(filePath, message, details) {
|
|
17
|
+
_bundleFailures.push({ filePath: filePath || "<inline>", message, details });
|
|
18
|
+
}
|
|
19
|
+
export function getBundleFailures() {
|
|
20
|
+
return _bundleFailures.slice();
|
|
21
|
+
}
|
|
22
|
+
export function clearBundleFailures() {
|
|
23
|
+
_bundleFailures.length = 0;
|
|
24
|
+
}
|
|
25
|
+
let _fallbackOverride;
|
|
26
|
+
export function setBundlerFallbackMode(mode) {
|
|
27
|
+
_fallbackOverride = mode;
|
|
28
|
+
}
|
|
29
|
+
export function resolveBundlerFallback() {
|
|
30
|
+
if (_fallbackOverride)
|
|
31
|
+
return _fallbackOverride;
|
|
32
|
+
try {
|
|
33
|
+
return config.strict?.bundlerFallback ?? "warn";
|
|
34
|
+
} catch {
|
|
35
|
+
return "warn";
|
|
36
|
+
}
|
|
37
|
+
}
|
|
14
38
|
const EXTERNAL_PATTERNS = [
|
|
15
39
|
/^stx$/,
|
|
16
40
|
/^@stacksjs\/stx$/,
|
|
@@ -268,7 +292,11 @@ ${publicAssignments}`.trim();
|
|
|
268
292
|
}));
|
|
269
293
|
return bundled;
|
|
270
294
|
} catch (error) {
|
|
271
|
-
|
|
295
|
+
const details = describeBuildFailure(error, filePath || void 0), failure = formatBuildFailure(error, filePath || void 0);
|
|
296
|
+
console.warn(`[stx:bundler] error: ${failure}`);
|
|
297
|
+
recordBundleFailure(filePath || "", failure, details);
|
|
298
|
+
if (resolveBundlerFallback() === "error")
|
|
299
|
+
throw error;
|
|
272
300
|
return code;
|
|
273
301
|
} finally {
|
|
274
302
|
try {
|
package/dist/client-script.d.ts
CHANGED
|
@@ -1,4 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client Script Processor
|
|
3
|
+
*
|
|
4
|
+
* Transforms <script client> blocks in SFC (.stx) components:
|
|
5
|
+
* 1. Auto-imports from 'stx' and '@stacksjs/browser' (no import statements needed)
|
|
6
|
+
* 2. Resolves `import { store } from '@stores'` to runtime store access
|
|
7
|
+
* 3. Transpiles TypeScript to JavaScript (strips type annotations, interfaces, import type)
|
|
8
|
+
* 4. Injects event binding code (from @click, @input, etc.) into the script scope
|
|
9
|
+
* 5. Auto-wraps in a scoped IIFE for isolation
|
|
10
|
+
*
|
|
11
|
+
* This enables clean component authoring:
|
|
12
|
+
* ```stx
|
|
13
|
+
* <template>
|
|
14
|
+
* <button @click="handleClick()">Count: {{ count() }}</button>
|
|
15
|
+
* </template>
|
|
16
|
+
*
|
|
17
|
+
* <script>
|
|
18
|
+
* // No imports needed! state, onMount, Trail, etc. are auto-imported
|
|
19
|
+
* const count = state(0)
|
|
20
|
+
*
|
|
21
|
+
* onMount(async () => {
|
|
22
|
+
* const trails = await Trail.all()
|
|
23
|
+
* console.log(trails)
|
|
24
|
+
* })
|
|
25
|
+
*
|
|
26
|
+
* function handleClick() {
|
|
27
|
+
* count.set(count() + 1)
|
|
28
|
+
* }
|
|
29
|
+
* </script>
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* @module client-script
|
|
33
|
+
*/
|
|
1
34
|
import type { ParsedEvent } from './events';
|
|
35
|
+
export interface VendorCssImport {
|
|
36
|
+
/** The original specifier as written in the import — kept verbatim for the data attribute. */
|
|
37
|
+
source: string;
|
|
38
|
+
/** The resolved absolute filesystem path — used for dedupe and reads. */
|
|
39
|
+
resolvedPath: string;
|
|
40
|
+
/** The CSS file contents at build time. */
|
|
41
|
+
contents: string;
|
|
42
|
+
}
|
|
2
43
|
/**
|
|
3
44
|
* Strip side-effect `.css` imports from a `<script client>` body and read
|
|
4
45
|
* their contents off disk.
|
|
@@ -21,7 +62,13 @@ import type { ParsedEvent } from './events';
|
|
|
21
62
|
* call. Cross-component dedupe still relies on the caller's HTML response
|
|
22
63
|
* (CSS rules collapse harmlessly at the browser level either way).
|
|
23
64
|
*/
|
|
24
|
-
export declare function extractAndStripCssImports(code: string, options: {
|
|
65
|
+
export declare function extractAndStripCssImports(code: string, options: {
|
|
66
|
+
filePath?: string;
|
|
67
|
+
projectRoot?: string;
|
|
68
|
+
}): {
|
|
69
|
+
code: string;
|
|
70
|
+
styles: VendorCssImport[];
|
|
71
|
+
};
|
|
25
72
|
/**
|
|
26
73
|
* Render collected vendor CSS as one `<style>` tag per resolved source,
|
|
27
74
|
* each wrapped in `@layer vendor { … }` so the styles can never out-specificity
|
|
@@ -33,6 +80,33 @@ export declare function extractAndStripCssImports(code: string, options: { fileP
|
|
|
33
80
|
* below un-layered rules, so vendor styles default to lowest precedence.
|
|
34
81
|
*/
|
|
35
82
|
export declare function renderVendorStyleTags(styles: VendorCssImport[]): string;
|
|
83
|
+
/**
|
|
84
|
+
* Exports from 'stx' auto-imported into classic `<script>` / `<script client>`
|
|
85
|
+
* blocks.
|
|
86
|
+
*
|
|
87
|
+
* Derived from {@link STX_RUNTIME_GLOBALS} rather than hand-maintained (#1804).
|
|
88
|
+
* This was a separate 82-name literal, and it had drifted: 16 of those names
|
|
89
|
+
* were not on `window.stx` at all. Because the generated prologue destructures
|
|
90
|
+
* (`var { … } = window.stx`), a missing name is not an error — it binds
|
|
91
|
+
* `undefined`, and the author sees "undefined is not a function" at the call
|
|
92
|
+
* site with nothing naming the identifier or saying why. `provide` worked and
|
|
93
|
+
* `inject` did not; `onMounted` worked and `onUpdated` did not.
|
|
94
|
+
*
|
|
95
|
+
* Five of the 16 (`isDerived`, `inject`, `useSlots`, `watchMultiple`,
|
|
96
|
+
* `onBeforeMount`) are now implemented in the runtime and live in the shared
|
|
97
|
+
* list. The remaining 11 are server- or compile-time only — `h` and `Fragment`
|
|
98
|
+
* belong to the JSX runtime, `useMeta`/`getCurrentInstance`/`useAttrs`/
|
|
99
|
+
* `onErrorCaptured`/`onBeforeUpdate`/`onUpdated`/`createStore`/`createSelector`
|
|
100
|
+
* are module exports with no client counterpart, and `action` has no
|
|
101
|
+
* implementation anywhere — so they are no longer offered. A `ReferenceError`
|
|
102
|
+
* naming the identifier beats a silent `undefined`.
|
|
103
|
+
*/
|
|
104
|
+
export declare const STX_AUTO_IMPORTS: readonly string[];
|
|
105
|
+
export interface BrowserCoreAutoImportResult {
|
|
106
|
+
code: string;
|
|
107
|
+
imports: string[];
|
|
108
|
+
models: string[];
|
|
109
|
+
}
|
|
36
110
|
/**
|
|
37
111
|
* Turn bare core browser helper usage into a real module import before the
|
|
38
112
|
* client bundler runs.
|
|
@@ -55,6 +129,33 @@ export declare function injectBrowserCoreAutoImports(code: string): BrowserCoreA
|
|
|
55
129
|
* undefined name.
|
|
56
130
|
*/
|
|
57
131
|
export declare function stripCommentsAndLiterals(code: string): string;
|
|
132
|
+
export interface ClientScriptOptions {
|
|
133
|
+
/** Event bindings collected from template @event attributes */
|
|
134
|
+
eventBindings?: ParsedEvent[];
|
|
135
|
+
/** Whether to enable auto-imports (default: true) */
|
|
136
|
+
autoImports?: boolean;
|
|
137
|
+
/** Original script tag attributes (e.g., 'type="module"') */
|
|
138
|
+
attrs?: string;
|
|
139
|
+
/** Whether the component template contains : prefix directives */
|
|
140
|
+
hasColonDirectives?: boolean;
|
|
141
|
+
/** Template HTML content for auto-binding analysis (Phase 2: auto-detect which scripts need stx.mount()) */
|
|
142
|
+
templateContent?: string;
|
|
143
|
+
/** File path of the template (for relative import resolution in bundler) */
|
|
144
|
+
filePath?: string;
|
|
145
|
+
/** Project root directory (for @/ path resolution in bundler) */
|
|
146
|
+
projectRoot?: string;
|
|
147
|
+
/** Whether this is a production build (enables minification in bundler) */
|
|
148
|
+
production?: boolean;
|
|
149
|
+
/**
|
|
150
|
+
* Server → client data bridge. Top-level `<script server>` variables (and
|
|
151
|
+
* other template-context values) made available to `<script client>` code:
|
|
152
|
+
* any JSON-serializable value referenced — but not redeclared — by the client
|
|
153
|
+
* script is serialized into the client bundle, so reactive state can be seeded
|
|
154
|
+
* from build-time data (e.g. `const items = state(serverItems)`) without a
|
|
155
|
+
* round-trip fetch. Functions/non-serializable values are skipped.
|
|
156
|
+
*/
|
|
157
|
+
serverData?: Record<string, unknown>;
|
|
158
|
+
}
|
|
58
159
|
/**
|
|
59
160
|
* Generate destructuring statements for auto-imported symbols
|
|
60
161
|
* These access globals set up by the STX runtime and @stacksjs/browser
|
|
@@ -98,49 +199,3 @@ export declare function generateServerDataBridge(code: string, serverData?: Reco
|
|
|
98
199
|
* @returns A complete `<script>...</script>` tag ready for browser injection
|
|
99
200
|
*/
|
|
100
201
|
export declare function processClientScript(scriptContent: string, options?: ClientScriptOptions): Promise<string>;
|
|
101
|
-
/**
|
|
102
|
-
* Exports from 'stx' auto-imported into classic `<script>` / `<script client>`
|
|
103
|
-
* blocks.
|
|
104
|
-
*
|
|
105
|
-
* Derived from {@link STX_RUNTIME_GLOBALS} rather than hand-maintained (#1804).
|
|
106
|
-
* This was a separate 82-name literal, and it had drifted: 16 of those names
|
|
107
|
-
* were not on `window.stx` at all. Because the generated prologue destructures
|
|
108
|
-
* (`var { … } = window.stx`), a missing name is not an error — it binds
|
|
109
|
-
* `undefined`, and the author sees "undefined is not a function" at the call
|
|
110
|
-
* site with nothing naming the identifier or saying why. `provide` worked and
|
|
111
|
-
* `inject` did not; `onMounted` worked and `onUpdated` did not.
|
|
112
|
-
*
|
|
113
|
-
* Five of the 16 (`isDerived`, `inject`, `useSlots`, `watchMultiple`,
|
|
114
|
-
* `onBeforeMount`) are now implemented in the runtime and live in the shared
|
|
115
|
-
* list. The remaining 11 are server- or compile-time only — `h` and `Fragment`
|
|
116
|
-
* belong to the JSX runtime, `useMeta`/`getCurrentInstance`/`useAttrs`/
|
|
117
|
-
* `onErrorCaptured`/`onBeforeUpdate`/`onUpdated`/`createStore`/`createSelector`
|
|
118
|
-
* are module exports with no client counterpart, and `action` has no
|
|
119
|
-
* implementation anywhere — so they are no longer offered. A `ReferenceError`
|
|
120
|
-
* naming the identifier beats a silent `undefined`.
|
|
121
|
-
*/
|
|
122
|
-
export declare const STX_AUTO_IMPORTS: readonly string[];
|
|
123
|
-
export declare interface VendorCssImport {
|
|
124
|
-
source: string
|
|
125
|
-
resolvedPath: string
|
|
126
|
-
contents: string
|
|
127
|
-
}
|
|
128
|
-
export declare interface BrowserCoreAutoImportResult {
|
|
129
|
-
code: string
|
|
130
|
-
imports: string[]
|
|
131
|
-
models: string[]
|
|
132
|
-
}
|
|
133
|
-
// =============================================================================
|
|
134
|
-
// Types
|
|
135
|
-
// =============================================================================
|
|
136
|
-
export declare interface ClientScriptOptions {
|
|
137
|
-
eventBindings?: ParsedEvent[]
|
|
138
|
-
autoImports?: boolean
|
|
139
|
-
attrs?: string
|
|
140
|
-
hasColonDirectives?: boolean
|
|
141
|
-
templateContent?: string
|
|
142
|
-
filePath?: string
|
|
143
|
-
projectRoot?: string
|
|
144
|
-
production?: boolean
|
|
145
|
-
serverData?: Record<string, unknown>
|
|
146
|
-
}
|
package/dist/client-script.js
CHANGED
|
@@ -467,7 +467,7 @@ function transformAutoImports(code) {
|
|
|
467
467
|
for (const symbol of STX_AUTO_IMPORTS) {
|
|
468
468
|
if (existingImports.has(symbol) || locallyDeclared.has(symbol))
|
|
469
469
|
continue;
|
|
470
|
-
if (new RegExp(`\\b${symbol}\\s*\\(`, "g").test(transformedCode))
|
|
470
|
+
if (new RegExp(`\\b${symbol}\\s*(?:<[^>]*>)?\\s*\\(`, "g").test(transformedCode))
|
|
471
471
|
usedStxImports.add(symbol);
|
|
472
472
|
}
|
|
473
473
|
for (const symbol of BROWSER_CORE_IMPORTS) {
|
|
@@ -522,6 +522,18 @@ function generateAutoImportGuard(stxImports, browserImports) {
|
|
|
522
522
|
`;
|
|
523
523
|
}
|
|
524
524
|
export function extractBridgeData(context) {
|
|
525
|
+
const declared = context.__stxClientPayload;
|
|
526
|
+
if (declared && typeof declared === "object") {
|
|
527
|
+
const picked = {};
|
|
528
|
+
for (const key in declared) {
|
|
529
|
+
const value = declared[key];
|
|
530
|
+
if (typeof value === "function")
|
|
531
|
+
continue;
|
|
532
|
+
picked[key] = value;
|
|
533
|
+
}
|
|
534
|
+
picked.__stxDeclaredPayload = !0;
|
|
535
|
+
return picked;
|
|
536
|
+
}
|
|
525
537
|
const out = {};
|
|
526
538
|
for (const key in context) {
|
|
527
539
|
if (key.startsWith("__") || key.startsWith("$"))
|
|
@@ -580,13 +592,13 @@ function warnAboutBridgedValue(name, json) {
|
|
|
580
592
|
export function generateServerDataBridge(code, serverData) {
|
|
581
593
|
if (!serverData)
|
|
582
594
|
return "";
|
|
583
|
-
const searchable = stripCommentsAndLiterals(code), lines = [];
|
|
595
|
+
const searchable = stripCommentsAndLiterals(code), declaredPayload = serverData.__stxDeclaredPayload === !0, lines = [];
|
|
584
596
|
for (const [name, value] of Object.entries(serverData)) {
|
|
585
597
|
if (!/^[A-Za-z_$][\w$]*$/.test(name))
|
|
586
598
|
continue;
|
|
587
599
|
if (name.startsWith("__") || name.startsWith("$"))
|
|
588
600
|
continue;
|
|
589
|
-
if (!referencesIdentifier(searchable, name))
|
|
601
|
+
if (!declaredPayload && !referencesIdentifier(searchable, name))
|
|
590
602
|
continue;
|
|
591
603
|
if (declaresClientIdentifier(code, name))
|
|
592
604
|
continue;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/** Run the codemods over one file's source. */
|
|
2
|
+
export declare function codemodSource(source: string, options?: CodemodOptions): CodemodFileResult;
|
|
3
|
+
/** Human-readable report. */
|
|
4
|
+
export declare function formatCodemodFindings(findings: CodemodFinding[], wrote: boolean): string;
|
|
5
|
+
export declare interface CodemodFinding {
|
|
6
|
+
file: string
|
|
7
|
+
line: number
|
|
8
|
+
rule: CodemodRule
|
|
9
|
+
applied: boolean
|
|
10
|
+
reason?: string
|
|
11
|
+
snippet: string
|
|
12
|
+
}
|
|
13
|
+
export declare interface CodemodFileResult {
|
|
14
|
+
code: string
|
|
15
|
+
findings: CodemodFinding[]
|
|
16
|
+
}
|
|
17
|
+
export declare interface CodemodOptions {
|
|
18
|
+
rules?: CodemodRule[]
|
|
19
|
+
file?: string
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Codemods for adopting stx primitives an app hand-rolled around (#1843).
|
|
23
|
+
*
|
|
24
|
+
* Two production apps built on stx reached past it into vanilla JS for things
|
|
25
|
+
* it already ships. The audit found the cause was discovery, not absence: one
|
|
26
|
+
* app called bare `confirm()` in five places and used `title=` thirteen times
|
|
27
|
+
* while `stxConfirm` and `x-tooltip` were both delivered and used zero times.
|
|
28
|
+
* A native fallback that WORKS is the hardest kind of gap to notice — nothing
|
|
29
|
+
* fails, so nothing prompts you to look.
|
|
30
|
+
*
|
|
31
|
+
* #1843 proposes both rewrites as "mechanical and safe". Neither is, and the
|
|
32
|
+
* difference matters enough that this tool reports by default and only edits
|
|
33
|
+
* when asked:
|
|
34
|
+
*
|
|
35
|
+
* - `confirm()` returns a boolean synchronously; `stxConfirm()` returns a
|
|
36
|
+
* Promise. `if (confirm(m))` rewritten to `if (stxConfirm(m))` is ALWAYS
|
|
37
|
+
* TRUE, because a Promise is truthy. A blind rewrite turns every "Delete
|
|
38
|
+
* this?" into an unconditional yes.
|
|
39
|
+
*
|
|
40
|
+
* - `title=` is announced by screen readers. `x-tooltip` sets no `role` and
|
|
41
|
+
* no `aria-*` — it is a visual tooltip driven by mouseover/focusin.
|
|
42
|
+
* Replacing one with the other removes the accessible description. So the
|
|
43
|
+
* tooltip rule ADDS `x-tooltip` and keeps `title`.
|
|
44
|
+
*/
|
|
45
|
+
export type CodemodRule = | 'confirm'
|
|
46
|
+
| 'tooltip'
|
|
47
|
+
| 'route-params'
|
|
48
|
+
| 'search-params'
|
|
49
|
+
| 'navigate'
|
|
50
|
+
| 'fetch'
|
|
51
|
+
| 'polling'
|
|
52
|
+
| 'debounce'
|
|
53
|
+
| 'click-outside'
|
|
54
|
+
| 'focus'
|
|
55
|
+
| 'clipboard'
|
|
56
|
+
| 'watch';
|
package/dist/codemod.js
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
function contextAfter(source, index, length = 240) {
|
|
2
|
+
return source.slice(index, index + length);
|
|
3
|
+
}
|
|
4
|
+
const REPORT_RULES = [
|
|
5
|
+
{
|
|
6
|
+
rule: "route-params",
|
|
7
|
+
pattern: /\blocation\s*\.\s*pathname\s*\.\s*(?:match|split)\s*\(/g,
|
|
8
|
+
primitive: "useRoute() / useRouteParams() / useRouteParam(name)",
|
|
9
|
+
why: "parsing the URL by hand re-implements the router and does not update on SPA navigation"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
rule: "search-params",
|
|
13
|
+
pattern: /new\s+URLSearchParams\s*\(/g,
|
|
14
|
+
primitive: "useSearchParams()",
|
|
15
|
+
why: "reactive, and writes back through the router instead of history.replaceState"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
rule: "navigate",
|
|
19
|
+
pattern: /\blocation\s*\.\s*(?:assign|replace)\s*\(|\blocation\s*\.\s*href\s*=/g,
|
|
20
|
+
primitive: "navigate(path) / goBack()",
|
|
21
|
+
why: "a full document load throws away every store and the scroll position; navigate() is an SPA transition",
|
|
22
|
+
reject: (source, index) => /^[^\n]*?(?:['"`]\s*)?(?:mailto:|tel:|sms:|https?:\/\/|\/\/)/.test(contextAfter(source, index, 160))
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
rule: "fetch",
|
|
26
|
+
pattern: /new\s+AbortController\s*\(/g,
|
|
27
|
+
primitive: "useFetch(url) / useAsyncData(key, fn)",
|
|
28
|
+
why: "cancellation, loading and error state are already wired; hand-rolling them is where the races live"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
rule: "polling",
|
|
32
|
+
pattern: /setInterval\s*\(/g,
|
|
33
|
+
primitive: "useEventSource() / useSSE() / useWebSocket() / useChannel()",
|
|
34
|
+
why: "polling burns requests and lags; the push primitives are delivered",
|
|
35
|
+
reject: (source, index) => !/\bfetch\s*\(|\$fetch\b|useFetch\b|\.get\s*\(|axios\b/.test(contextAfter(source, index))
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
rule: "debounce",
|
|
39
|
+
pattern: /\bclearTimeout\s*\(/g,
|
|
40
|
+
primitive: "useDebounce(fn, ms) / useThrottle(fn, ms)",
|
|
41
|
+
why: "a hand-held timer handle per call site is the usual source of stale-result bugs",
|
|
42
|
+
requires: (source) => /setTimeout\s*\(/.test(source)
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
rule: "click-outside",
|
|
46
|
+
pattern: /document\s*\.\s*addEventListener\s*\(\s*['"](?:click|mousedown|pointerdown)['"]/g,
|
|
47
|
+
primitive: "useClickOutside(ref, fn)",
|
|
48
|
+
why: "the document-level listener plus the blur race is exactly what this composable exists to remove"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
rule: "focus",
|
|
52
|
+
pattern: /\.\s*(?:getElementById|querySelector)\s*\([^)]*\)\s*(?:\?\.)?\s*\.?focus\s*\(/g,
|
|
53
|
+
primitive: "useFocus(ref) with x-ref",
|
|
54
|
+
why: "a query by id breaks under nested scopes and SPA navigation; a ref does not"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
rule: "clipboard",
|
|
58
|
+
pattern: /navigator\s*\.\s*(?:clipboard|share)\b/g,
|
|
59
|
+
primitive: "useClipboard() / useShare()",
|
|
60
|
+
why: "permission state, the copied flag and the unsupported-browser fallback are already handled"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
rule: "watch",
|
|
64
|
+
pattern: /\beffect\s*\(\s*\(\s*\)\s*=>\s*\{[^}]*\bvoid\s+\w+\s*\(\s*\)/g,
|
|
65
|
+
primitive: "watch(source, cb) / watchMultiple([...], cb)",
|
|
66
|
+
why: "the `void a(); void b()` dance is a manual dependency list; watch takes one directly"
|
|
67
|
+
}
|
|
68
|
+
], DEFAULT_RULES = ["confirm", "tooltip", ...REPORT_RULES.map((r) => r.rule)];
|
|
69
|
+
function lineOf(source, index) {
|
|
70
|
+
let line = 1;
|
|
71
|
+
for (let i = 0;i < index; i++)
|
|
72
|
+
if (source[i] === `
|
|
73
|
+
`)
|
|
74
|
+
line++;
|
|
75
|
+
return line;
|
|
76
|
+
}
|
|
77
|
+
function snippetAt(source, index) {
|
|
78
|
+
const start = source.lastIndexOf(`
|
|
79
|
+
`, index) + 1, end = source.indexOf(`
|
|
80
|
+
`, index);
|
|
81
|
+
return source.slice(start, end === -1 ? source.length : end).trim().slice(0, 120);
|
|
82
|
+
}
|
|
83
|
+
function isInsideAsyncFunction(source, index) {
|
|
84
|
+
const before = source.slice(0, index), fnKeyword = before.lastIndexOf("function"), arrow = before.lastIndexOf("=>"), boundary = Math.max(fnKeyword, arrow);
|
|
85
|
+
if (boundary === -1)
|
|
86
|
+
return !1;
|
|
87
|
+
const window = source.slice(Math.max(0, boundary - 120), boundary);
|
|
88
|
+
return /\basync\b[^;{}]*$/.test(window);
|
|
89
|
+
}
|
|
90
|
+
function applyConfirmRule(source, file, findings) {
|
|
91
|
+
const pattern = /(^|[^\w$.])confirm\s*\(/g;
|
|
92
|
+
let out = "", last = 0, match;
|
|
93
|
+
while ((match = pattern.exec(source)) !== null) {
|
|
94
|
+
const callAt = match.index + match[1].length, line = lineOf(source, callAt), snippet = snippetAt(source, callAt);
|
|
95
|
+
if (/stxConfirm\s*\($/.test(source.slice(0, callAt + 8)))
|
|
96
|
+
continue;
|
|
97
|
+
const safe = isInsideAsyncFunction(source, callAt);
|
|
98
|
+
findings.push({
|
|
99
|
+
file,
|
|
100
|
+
line,
|
|
101
|
+
rule: "confirm",
|
|
102
|
+
applied: safe,
|
|
103
|
+
snippet,
|
|
104
|
+
reason: safe ? void 0 : "stxConfirm() returns a Promise and native confirm() is synchronous, so this needs `await` inside an async function. Rewriting it here would make the condition always true."
|
|
105
|
+
});
|
|
106
|
+
if (safe) {
|
|
107
|
+
out += source.slice(last, callAt) + "await stxConfirm(";
|
|
108
|
+
last = callAt + 8;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return out + source.slice(last);
|
|
112
|
+
}
|
|
113
|
+
function applyTooltipRule(source, file, findings) {
|
|
114
|
+
const pattern = /(<[a-z][\w-]*\b[^>]*?)\stitle=(["'])(.*?)\2/gis;
|
|
115
|
+
let out = "", last = 0, match;
|
|
116
|
+
while ((match = pattern.exec(source)) !== null) {
|
|
117
|
+
const [whole, openTag, quote, value] = match, line = lineOf(source, match.index), snippet = snippetAt(source, match.index);
|
|
118
|
+
if (/\bx-tooltip[=\s]/.test(openTag) || value.trim() === "") {
|
|
119
|
+
findings.push({
|
|
120
|
+
file,
|
|
121
|
+
line,
|
|
122
|
+
rule: "tooltip",
|
|
123
|
+
applied: !1,
|
|
124
|
+
snippet,
|
|
125
|
+
reason: /\bx-tooltip[=\s]/.test(openTag) ? "already has x-tooltip" : "empty title"
|
|
126
|
+
});
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
findings.push({ file, line, rule: "tooltip", applied: !0, snippet });
|
|
130
|
+
out += source.slice(last, match.index) + `${openTag} x-tooltip=${quote}${value}${quote} title=${quote}${value}${quote}`;
|
|
131
|
+
last = match.index + whole.length;
|
|
132
|
+
}
|
|
133
|
+
return out + source.slice(last);
|
|
134
|
+
}
|
|
135
|
+
function applyReportRules(source, file, rules, findings) {
|
|
136
|
+
for (const entry of REPORT_RULES) {
|
|
137
|
+
if (!rules.includes(entry.rule))
|
|
138
|
+
continue;
|
|
139
|
+
if (entry.requires && !entry.requires(source))
|
|
140
|
+
continue;
|
|
141
|
+
entry.pattern.lastIndex = 0;
|
|
142
|
+
let match;
|
|
143
|
+
while ((match = entry.pattern.exec(source)) !== null) {
|
|
144
|
+
if (entry.reject?.(source, match.index))
|
|
145
|
+
continue;
|
|
146
|
+
findings.push({
|
|
147
|
+
file,
|
|
148
|
+
line: lineOf(source, match.index),
|
|
149
|
+
rule: entry.rule,
|
|
150
|
+
applied: !1,
|
|
151
|
+
snippet: snippetAt(source, match.index),
|
|
152
|
+
reason: `${entry.primitive} \u2014 ${entry.why}`
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
export function codemodSource(source, options = {}) {
|
|
158
|
+
const rules = options.rules ?? DEFAULT_RULES, file = options.file ?? "<source>", findings = [];
|
|
159
|
+
applyReportRules(source, file, rules, findings);
|
|
160
|
+
let code = source;
|
|
161
|
+
if (rules.includes("confirm"))
|
|
162
|
+
code = applyConfirmRule(code, file, findings);
|
|
163
|
+
if (rules.includes("tooltip"))
|
|
164
|
+
code = applyTooltipRule(code, file, findings);
|
|
165
|
+
findings.sort((a, b) => a.line - b.line);
|
|
166
|
+
return { code, findings };
|
|
167
|
+
}
|
|
168
|
+
const REWRITABLE = new Set(["confirm", "tooltip"]);
|
|
169
|
+
export function formatCodemodFindings(findings, wrote) {
|
|
170
|
+
if (findings.length === 0)
|
|
171
|
+
return "No hand-rolled equivalents found.";
|
|
172
|
+
const applied = findings.filter((f) => f.applied), declined = findings.filter((f) => !f.applied && REWRITABLE.has(f.rule)), adopt = findings.filter((f) => !f.applied && !REWRITABLE.has(f.rule)), lines = [];
|
|
173
|
+
if (applied.length > 0) {
|
|
174
|
+
lines.push(wrote ? `Rewrote ${applied.length} site(s):` : `Would rewrite ${applied.length} site(s) (pass --fix):`);
|
|
175
|
+
for (const f of applied)
|
|
176
|
+
lines.push(` ${f.file}:${f.line} [${f.rule}] ${f.snippet}`);
|
|
177
|
+
}
|
|
178
|
+
if (declined.length > 0) {
|
|
179
|
+
lines.push("", `Needs a human \u2014 ${declined.length} site(s):`);
|
|
180
|
+
for (const f of declined)
|
|
181
|
+
lines.push(` ${f.file}:${f.line} [${f.rule}] ${f.snippet}
|
|
182
|
+
${f.reason}`);
|
|
183
|
+
}
|
|
184
|
+
if (adopt.length > 0) {
|
|
185
|
+
lines.push("", `An stx primitive already covers this \u2014 ${adopt.length} site(s):`);
|
|
186
|
+
for (const f of adopt)
|
|
187
|
+
lines.push(` ${f.file}:${f.line} [${f.rule}] ${f.snippet}
|
|
188
|
+
use ${f.reason}`);
|
|
189
|
+
}
|
|
190
|
+
return lines.join(`
|
|
191
|
+
`);
|
|
192
|
+
}
|