@frmwrkd/snag-anchor 0.1.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.
Files changed (75) hide show
  1. package/LICENSE +19 -0
  2. package/README.md +305 -0
  3. package/dist/babel/component-name.d.ts +42 -0
  4. package/dist/babel/component-name.d.ts.map +1 -0
  5. package/dist/babel/component-name.js +107 -0
  6. package/dist/babel/component-name.js.map +1 -0
  7. package/dist/babel/plugin.d.ts +69 -0
  8. package/dist/babel/plugin.d.ts.map +1 -0
  9. package/dist/babel/plugin.js +272 -0
  10. package/dist/babel/plugin.js.map +1 -0
  11. package/dist/babel/walk.d.ts +8 -0
  12. package/dist/babel/walk.d.ts.map +1 -0
  13. package/dist/babel/walk.js +50 -0
  14. package/dist/babel/walk.js.map +1 -0
  15. package/dist/devtools-json.d.ts +45 -0
  16. package/dist/devtools-json.d.ts.map +1 -0
  17. package/dist/devtools-json.js +117 -0
  18. package/dist/devtools-json.js.map +1 -0
  19. package/dist/esbuild.d.ts +3 -0
  20. package/dist/esbuild.d.ts.map +1 -0
  21. package/dist/esbuild.js +11 -0
  22. package/dist/esbuild.js.map +1 -0
  23. package/dist/index.d.ts +31 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +90 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/loader.cjs +44 -0
  28. package/dist/loader.cjs.map +1 -0
  29. package/dist/loader.d.cts +2 -0
  30. package/dist/loader.d.cts.map +1 -0
  31. package/dist/magic-string.d.ts +23 -0
  32. package/dist/magic-string.d.ts.map +1 -0
  33. package/dist/magic-string.js +23 -0
  34. package/dist/magic-string.js.map +1 -0
  35. package/dist/nuxt.d.ts +14 -0
  36. package/dist/nuxt.d.ts.map +1 -0
  37. package/dist/nuxt.js +29 -0
  38. package/dist/nuxt.js.map +1 -0
  39. package/dist/options.d.ts +101 -0
  40. package/dist/options.d.ts.map +1 -0
  41. package/dist/options.js +122 -0
  42. package/dist/options.js.map +1 -0
  43. package/dist/rollup.d.ts +3 -0
  44. package/dist/rollup.d.ts.map +1 -0
  45. package/dist/rollup.js +11 -0
  46. package/dist/rollup.js.map +1 -0
  47. package/dist/root.d.ts +38 -0
  48. package/dist/root.d.ts.map +1 -0
  49. package/dist/root.js +101 -0
  50. package/dist/root.js.map +1 -0
  51. package/dist/rspack.d.ts +3 -0
  52. package/dist/rspack.d.ts.map +1 -0
  53. package/dist/rspack.js +11 -0
  54. package/dist/rspack.js.map +1 -0
  55. package/dist/stamp.d.ts +66 -0
  56. package/dist/stamp.d.ts.map +1 -0
  57. package/dist/stamp.js +28 -0
  58. package/dist/stamp.js.map +1 -0
  59. package/dist/transform.d.ts +14 -0
  60. package/dist/transform.d.ts.map +1 -0
  61. package/dist/transform.js +54 -0
  62. package/dist/transform.js.map +1 -0
  63. package/dist/vite.d.ts +3 -0
  64. package/dist/vite.d.ts.map +1 -0
  65. package/dist/vite.js +11 -0
  66. package/dist/vite.js.map +1 -0
  67. package/dist/vue/transform.d.ts +50 -0
  68. package/dist/vue/transform.d.ts.map +1 -0
  69. package/dist/vue/transform.js +188 -0
  70. package/dist/vue/transform.js.map +1 -0
  71. package/dist/webpack.d.ts +3 -0
  72. package/dist/webpack.d.ts.map +1 -0
  73. package/dist/webpack.js +15 -0
  74. package/dist/webpack.js.map +1 -0
  75. package/package.json +84 -0
package/dist/root.js ADDED
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Repo-root detection and path normalisation.
3
+ *
4
+ * doc 02 §2.1: "Paths are **repo-relative and POSIX-normalised**. The absolute
5
+ * project root is resolved separately (§2.5), so the same attribute works
6
+ * whether the app runs locally, in a container, or on a preview deploy."
7
+ *
8
+ * Two invariants this module exists to hold:
9
+ *
10
+ * 1. **Every emitted path is repo-relative.** An absolute path baked into a
11
+ * `data-snag` attribute is machine-specific: it breaks in a container, it
12
+ * breaks for a teammate, and it leaks the developer's home directory into
13
+ * the DOM. The absolute root is served once, out of band, by
14
+ * `devtools-json.ts`.
15
+ * 2. **Every emitted path is POSIX-normalised.** `src\checkout\PromoField.tsx`
16
+ * and `src/checkout/PromoField.tsx` must be the same string, or a capture
17
+ * filed on Windows will not match a repo map built on Linux and
18
+ * `suspect_files` silently stops resolving.
19
+ */
20
+ import { existsSync } from 'node:fs';
21
+ import path from 'node:path';
22
+ /**
23
+ * Backslashes → forward slashes. Applied to every path this package emits.
24
+ *
25
+ * Note this is deliberately *not* `path.posix.normalize` — we do not want to
26
+ * collapse `..` segments or otherwise rewrite a path the caller gave us, only
27
+ * to make the separator identical on every platform.
28
+ */
29
+ export function toPosix(p) {
30
+ return p.replace(/\\/g, '/');
31
+ }
32
+ /** Cache keyed by the resolved start directory. Transform runs per file. */
33
+ const gitRootCache = new Map();
34
+ const manifestDirCache = new Map();
35
+ /** Test seam — the caches are process-global and would otherwise leak between tests. */
36
+ export function clearRootCaches() {
37
+ gitRootCache.clear();
38
+ manifestDirCache.clear();
39
+ }
40
+ function findUp(startDir, entry, cache) {
41
+ const start = path.resolve(startDir);
42
+ if (cache.has(start))
43
+ return cache.get(start);
44
+ let dir = start;
45
+ for (;;) {
46
+ if (existsSync(path.join(dir, entry))) {
47
+ cache.set(start, dir);
48
+ return dir;
49
+ }
50
+ const parent = path.dirname(dir);
51
+ if (parent === dir)
52
+ break;
53
+ dir = parent;
54
+ }
55
+ cache.set(start, undefined);
56
+ return undefined;
57
+ }
58
+ /**
59
+ * Nearest ancestor directory containing `.git`.
60
+ *
61
+ * `.git` is tested with `existsSync`, not `isDirectory`, on purpose: in a git
62
+ * worktree or a submodule `.git` is a *file* containing a `gitdir:` pointer.
63
+ * Treating it as "not a repo" would push the root up to the parent checkout and
64
+ * every emitted path would gain a bogus prefix.
65
+ */
66
+ export function findGitRoot(startDir) {
67
+ return findUp(startDir, '.git', gitRootCache);
68
+ }
69
+ /** Nearest ancestor directory containing `package.json`. Fallback when there is no git repo. */
70
+ export function findManifestDir(startDir) {
71
+ return findUp(startDir, 'package.json', manifestDirCache);
72
+ }
73
+ /**
74
+ * The project root every emitted path is relative to.
75
+ *
76
+ * Precedence: explicit `root` option → git root → nearest `package.json` →
77
+ * the working directory. Always absolute and always POSIX-normalised.
78
+ */
79
+ export function resolveRoot(explicitRoot, cwd = process.cwd()) {
80
+ if (explicitRoot !== undefined && explicitRoot !== '') {
81
+ return toPosix(path.resolve(explicitRoot));
82
+ }
83
+ const found = findGitRoot(cwd) ?? findManifestDir(cwd) ?? cwd;
84
+ return toPosix(path.resolve(found));
85
+ }
86
+ /**
87
+ * `/Users/theo/dev/app/src/App.tsx` + root `/Users/theo/dev/app` →
88
+ * `src/App.tsx`.
89
+ *
90
+ * A file outside the root keeps its `../` prefix rather than being silently
91
+ * rewritten — `root + '/' + '../lib/x.tsx'` still resolves to the right file,
92
+ * and inventing a path is the one failure mode doc 00 §3 forbids outright.
93
+ */
94
+ export function toRepoRelative(absoluteFile, root) {
95
+ const abs = path.resolve(absoluteFile);
96
+ const rel = toPosix(path.relative(root, abs));
97
+ if (rel === '')
98
+ return toPosix(path.basename(abs));
99
+ return rel.startsWith('./') ? rel.slice(2) : rel;
100
+ }
101
+ //# sourceMappingURL=root.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"root.js","sourceRoot":"","sources":["../src/root.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ declare const _default: (options?: import("./options.js").AnchorOptions | undefined) => RspackPluginInstance;
2
+ export default _default;
3
+ //# sourceMappingURL=rspack.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rspack.d.ts","sourceRoot":"","sources":["../src/rspack.ts"],"names":[],"mappings":";AAUA,wBAAmD"}
package/dist/rspack.js ADDED
@@ -0,0 +1,11 @@
1
+ /**
2
+ * ```js
3
+ * // rspack.config.js
4
+ * import snagAnchor from '@frmwrkd/snag-anchor/rspack';
5
+ * export default { plugins: [snagAnchor()] };
6
+ * ```
7
+ */
8
+ import { createRspackPlugin } from 'unplugin';
9
+ import { unpluginFactory } from './index.js';
10
+ export default createRspackPlugin(unpluginFactory);
11
+ //# sourceMappingURL=rspack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rspack.js","sourceRoot":"","sources":["../src/rspack.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,eAAe,kBAAkB,CAAC,eAAe,CAAC,CAAC"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * What a stamp *is*, independent of the language being stamped.
3
+ *
4
+ * doc 02 §2.1 — two attributes only, self-contained, no global registry:
5
+ *
6
+ * ```html
7
+ * <button data-snag="src/checkout/PromoField.tsx:42:7" data-snag-cmp="PromoField">
8
+ * ```
9
+ *
10
+ * LocatorJS's default `data-locatorjs-id` mode requires a
11
+ * `window.__LOCATOR_DATA__` lookup table. Do not copy that: only `data-*`
12
+ * attributes cross the ISOLATED-world boundary, so an indirection through a
13
+ * page global would cost the resolver a `postMessage` round trip and would
14
+ * throw tier 0's entire advantage away.
15
+ *
16
+ * The JSX path (`babel/plugin.ts`) and the Vue path (`vue/transform.ts`) share
17
+ * this module so the emitted attribute set cannot diverge between frameworks.
18
+ */
19
+ import type { MagicString } from './magic-string.js';
20
+ import type { AnchorMode } from './options.js';
21
+ /** Whatever `MagicString#generateMap` produces — a standard v3 source map. */
22
+ export type AnchorSourceMap = ReturnType<MagicString['generateMap']>;
23
+ export interface StampContext {
24
+ /** Repo-relative, POSIX-normalised path of the file being transformed. */
25
+ file: string;
26
+ attribute: string;
27
+ componentAttribute: string;
28
+ intrinsicOnly: boolean;
29
+ mode: AnchorMode;
30
+ }
31
+ export interface AnchorAttribute {
32
+ name: string;
33
+ value: string;
34
+ }
35
+ /** One element that will be stamped, with everything needed to verify it. */
36
+ export interface StampSite {
37
+ /** Offset in the *original* source where the attribute text is inserted. */
38
+ offset: number;
39
+ /** 1-based line of the `<` of the opening tag. */
40
+ line: number;
41
+ /** 1-based column of the `<` of the opening tag. */
42
+ col: number;
43
+ /** Tag exactly as written in the source, e.g. `div`, `my-widget`. */
44
+ tag: string;
45
+ component: string | undefined;
46
+ attributes: AnchorAttribute[];
47
+ }
48
+ /**
49
+ * `'minimal'` emits the component name and **no path** — doc 02 §2.3. With no
50
+ * resolvable component name it therefore emits nothing at all, which is the
51
+ * point: a staging build should leak a name or nothing, never a directory tree.
52
+ */
53
+ export declare function buildAttributes(site: {
54
+ line: number;
55
+ col: number;
56
+ component: string | undefined;
57
+ }, ctx: StampContext): AnchorAttribute[];
58
+ /**
59
+ * JSX string-literal attribute values and HTML attribute values are both
60
+ * entity-decoded by their parsers, so `&` and `"` have to be escaped on the way
61
+ * in or a path containing either would come back out different.
62
+ */
63
+ export declare function escapeAttributeValue(value: string): string;
64
+ /** ` data-snag="…" data-snag-cmp="…"` — the exact text spliced into the source. */
65
+ export declare function renderAttributes(attributes: readonly AnchorAttribute[]): string;
66
+ //# sourceMappingURL=stamp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stamp.d.ts","sourceRoot":"","sources":["../src/stamp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,8EAA8E;AAC9E,MAAM,MAAM,eAAe,GAAG,UAAU,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC,CAAC;AAErE,MAAM,WAAW,YAAY;IAC3B,0EAA0E;IAC1E,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,aAAa,EAAE,OAAO,CAAC;IACvB,IAAI,EAAE,UAAU,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,6EAA6E;AAC7E,MAAM,WAAW,SAAS;IACxB,4EAA4E;IAC5E,MAAM,EAAE,MAAM,CAAC;IACf,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,oDAAoD;IACpD,GAAG,EAAE,MAAM,CAAC;IACZ,qEAAqE;IACrE,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,UAAU,EAAE,eAAe,EAAE,CAAC;CAC/B;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,EAClE,GAAG,EAAE,YAAY,GAChB,eAAe,EAAE,CAWnB;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,mFAAmF;AACnF,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,SAAS,eAAe,EAAE,GAAG,MAAM,CAE/E"}
package/dist/stamp.js ADDED
@@ -0,0 +1,28 @@
1
+ /**
2
+ * `'minimal'` emits the component name and **no path** — doc 02 §2.3. With no
3
+ * resolvable component name it therefore emits nothing at all, which is the
4
+ * point: a staging build should leak a name or nothing, never a directory tree.
5
+ */
6
+ export function buildAttributes(site, ctx) {
7
+ const attributes = [];
8
+ if (ctx.mode === 'full') {
9
+ attributes.push({ name: ctx.attribute, value: `${ctx.file}:${site.line}:${site.col}` });
10
+ }
11
+ if (site.component !== undefined && site.component !== '') {
12
+ attributes.push({ name: ctx.componentAttribute, value: site.component });
13
+ }
14
+ return attributes;
15
+ }
16
+ /**
17
+ * JSX string-literal attribute values and HTML attribute values are both
18
+ * entity-decoded by their parsers, so `&` and `"` have to be escaped on the way
19
+ * in or a path containing either would come back out different.
20
+ */
21
+ export function escapeAttributeValue(value) {
22
+ return value.replace(/&/g, '&amp;').replace(/"/g, '&quot;');
23
+ }
24
+ /** ` data-snag="…" data-snag-cmp="…"` — the exact text spliced into the source. */
25
+ export function renderAttributes(attributes) {
26
+ return attributes.map((attr) => ` ${attr.name}="${escapeAttributeValue(attr.value)}"`).join('');
27
+ }
28
+ //# sourceMappingURL=stamp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stamp.js","sourceRoot":"","sources":["../src/stamp.ts"],"names":[],"mappings":"AAoDA;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAC7B,IAAkE,EAClE,GAAiB;IAEjB,MAAM,UAAU,GAAsB,EAAE,CAAC;IAEzC,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QACxB,UAAU,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,SAAS,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAC1F,CAAC;IACD,IAAI,IAAI,CAAC,SAAS,KAAK,SAAS,IAAI,IAAI,CAAC,SAAS,KAAK,EAAE,EAAE,CAAC;QAC1D,UAAU,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,kBAAkB,EAAE,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;IAC3E,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAa;IAChD,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC9D,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,gBAAgB,CAAC,UAAsC;IACrE,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,IAAI,KAAK,oBAAoB,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAClG,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { ResolvedAnchorOptions } from './options.js';
2
+ import type { AnchorSourceMap, StampContext, StampSite } from './stamp.js';
3
+ export interface AnchorTransformResult {
4
+ code: string;
5
+ map: AnchorSourceMap;
6
+ sites: StampSite[];
7
+ }
8
+ export declare function stampContextFor(id: string, options: ResolvedAnchorOptions): StampContext | null;
9
+ /**
10
+ * `null` means "leave this file exactly as it was" — not included, not a
11
+ * language we stamp, nothing to stamp, or unparseable. Never throws.
12
+ */
13
+ export declare function transformSource(code: string, id: string, options: ResolvedAnchorOptions): AnchorTransformResult | null;
14
+ //# sourceMappingURL=transform.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../src/transform.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAE1D,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG3E,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,eAAe,CAAC;IACrB,KAAK,EAAE,SAAS,EAAE,CAAC;CACpB;AAKD,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,qBAAqB,GAAG,YAAY,GAAG,IAAI,CAW/F;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,qBAAqB,GAC7B,qBAAqB,GAAG,IAAI,CA0B9B"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * One entry point that every bundler — and the Turbopack/webpack loader —
3
+ * routes through, so the JSX path and the Vue path can never be wired up
4
+ * differently by two different integrations.
5
+ */
6
+ import path from 'node:path';
7
+ import { stampJsx } from './babel/plugin.js';
8
+ import { normaliseId } from './options.js';
9
+ import { toRepoRelative } from './root.js';
10
+ import { stampVue } from './vue/transform.js';
11
+ /** Extensions the JSX path knows how to parse. */
12
+ const JSX_EXTENSIONS = new Set(['.jsx', '.tsx', '.js', '.mjs', '.cjs', '.ts', '.mts', '.cts']);
13
+ export function stampContextFor(id, options) {
14
+ const file = normaliseId(id);
15
+ if (file === null)
16
+ return null;
17
+ return {
18
+ file: toRepoRelative(file, options.root),
19
+ attribute: options.attribute,
20
+ componentAttribute: options.componentAttribute,
21
+ intrinsicOnly: options.intrinsicOnly,
22
+ mode: options.mode,
23
+ };
24
+ }
25
+ /**
26
+ * `null` means "leave this file exactly as it was" — not included, not a
27
+ * language we stamp, nothing to stamp, or unparseable. Never throws.
28
+ */
29
+ export function transformSource(code, id, options) {
30
+ if (options.mode === 'off')
31
+ return null;
32
+ if (!options.filter(id))
33
+ return null;
34
+ const file = normaliseId(id);
35
+ if (file === null)
36
+ return null;
37
+ const ctx = stampContextFor(id, options);
38
+ if (ctx === null)
39
+ return null;
40
+ const ext = path.posix.extname(file).toLowerCase();
41
+ if (ext === '.vue') {
42
+ return stampVue(code, ctx, file);
43
+ }
44
+ // Svelte 5 already attaches `element.__svelte_meta`; resolver tier 1 reads it
45
+ // straight off the node. Injecting here would duplicate framework metadata
46
+ // that can only disagree with it. doc 02 §2.2.
47
+ if (ext === '.svelte')
48
+ return null;
49
+ if (JSX_EXTENSIONS.has(ext)) {
50
+ return stampJsx(code, ctx, file);
51
+ }
52
+ return null;
53
+ }
54
+ //# sourceMappingURL=transform.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transform.js","sourceRoot":"","sources":["../src/transform.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAE3C,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAQ9C,kDAAkD;AAClD,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAE/F,MAAM,UAAU,eAAe,CAAC,EAAU,EAAE,OAA8B;IACxE,MAAM,IAAI,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;IAC7B,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAE/B,OAAO;QACL,IAAI,EAAE,cAAc,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC;QACxC,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,kBAAkB,EAAE,OAAO,CAAC,kBAAkB;QAC9C,aAAa,EAAE,OAAO,CAAC,aAAa;QACpC,IAAI,EAAE,OAAO,CAAC,IAAI;KACnB,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAC7B,IAAY,EACZ,EAAU,EACV,OAA8B;IAE9B,IAAI,OAAO,CAAC,IAAI,KAAK,KAAK;QAAE,OAAO,IAAI,CAAC;IACxC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAAE,OAAO,IAAI,CAAC;IAErC,MAAM,IAAI,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;IAC7B,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAE/B,MAAM,GAAG,GAAG,eAAe,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;IACzC,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAE9B,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;IAEnD,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC;QACnB,OAAO,QAAQ,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;IACnC,CAAC;IAED,8EAA8E;IAC9E,2EAA2E;IAC3E,+CAA+C;IAC/C,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAEnC,IAAI,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;QAC5B,OAAO,QAAQ,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;IACnC,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
package/dist/vite.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ declare const _default: (options?: import("./options.js").AnchorOptions | undefined) => import("unplugin").VitePlugin<any> | import("unplugin").VitePlugin<any>[];
2
+ export default _default;
3
+ //# sourceMappingURL=vite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite.d.ts","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":";AAUA,wBAAiD"}
package/dist/vite.js ADDED
@@ -0,0 +1,11 @@
1
+ /**
2
+ * ```ts
3
+ * // vite.config.ts
4
+ * import snagAnchor from '@frmwrkd/snag-anchor/vite';
5
+ * export default defineConfig({ plugins: [snagAnchor()] });
6
+ * ```
7
+ */
8
+ import { createVitePlugin } from 'unplugin';
9
+ import { unpluginFactory } from './index.js';
10
+ export default createVitePlugin(unpluginFactory);
11
+ //# sourceMappingURL=vite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite.js","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,eAAe,gBAAgB,CAAC,eAAe,CAAC,CAAC"}
@@ -0,0 +1,50 @@
1
+ import type { AnchorSourceMap, StampContext, StampSite } from '../stamp.js';
2
+ interface VuePosition {
3
+ line: number;
4
+ column: number;
5
+ offset: number;
6
+ }
7
+ interface VueLocation {
8
+ start: VuePosition;
9
+ end: VuePosition;
10
+ }
11
+ interface VueProp {
12
+ type: number;
13
+ name: string;
14
+ loc: VueLocation;
15
+ /** Directives only: `v-bind:data-snag` → `arg.content === 'data-snag'`. */
16
+ arg?: {
17
+ content?: string;
18
+ isStatic?: boolean;
19
+ } | undefined;
20
+ }
21
+ interface VueNode {
22
+ type: number;
23
+ tag?: string;
24
+ tagType?: number;
25
+ props?: VueProp[];
26
+ children?: VueNode[];
27
+ loc: VueLocation;
28
+ }
29
+ export interface VueStampResult {
30
+ code: string;
31
+ map: AnchorSourceMap;
32
+ sites: StampSite[];
33
+ }
34
+ /**
35
+ * The component name for an SFC is the file's base name — `App.vue` → `App`.
36
+ *
37
+ * An SFC *is* a component, so unlike JSX there is no enclosing scope to walk;
38
+ * this is the same convention Vue itself uses to infer a component's name when
39
+ * none is declared.
40
+ */
41
+ export declare function sfcComponentName(file: string): string | undefined;
42
+ export declare function collectVueStampSites(root: VueNode, ctx: StampContext): StampSite[];
43
+ /**
44
+ * Returns `null` when there is nothing to do — no template, a non-HTML template
45
+ * language, a parse failure, or no stampable element. Like the JSX path, this
46
+ * never throws: a broken dev server costs far more than a missing anchor.
47
+ */
48
+ export declare function stampVue(code: string, ctx: StampContext, filename?: string): VueStampResult | null;
49
+ export {};
50
+ //# sourceMappingURL=transform.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../../src/vue/transform.ts"],"names":[],"mappings":"AAwCA,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAuB5E,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,WAAW;IACnB,KAAK,EAAE,WAAW,CAAC;IACnB,GAAG,EAAE,WAAW,CAAC;CAClB;AAED,UAAU,OAAO;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,WAAW,CAAC;IACjB,2EAA2E;IAC3E,GAAG,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,SAAS,CAAC;CAC5D;AAED,UAAU,OAAO;IACf,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;IACrB,GAAG,EAAE,WAAW,CAAC;CAClB;AAID,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,eAAe,CAAC;IACrB,KAAK,EAAE,SAAS,EAAE,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAIjE;AA8CD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,YAAY,GAAG,SAAS,EAAE,CA2BlF;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAmClG"}
@@ -0,0 +1,188 @@
1
+ /**
2
+ * Vue SFC template transform — the same two attributes, from the same
3
+ * `StampContext`, so a Vue app and a React app hand the resolver identical
4
+ * strings (doc 02 §2.2).
5
+ *
6
+ * ## Why the SFC source is spliced rather than the render function patched
7
+ *
8
+ * `@vue/compiler-sfc`'s `parse()` hands back `descriptor.template.ast`, whose
9
+ * `loc.start.offset` / `.line` / `.column` are **absolute within the whole
10
+ * `.vue` file**, not relative to the template block. That is exactly what tier
11
+ * 0 needs — the line an editor opens is the line in `App.vue`, counted from the
12
+ * top of `App.vue` including the `<script>` block above the template. So this
13
+ * runs `enforce: 'pre'`, inserts attributes into the raw SFC text, and hands
14
+ * the modified SFC to `@vitejs/plugin-vue` as if the developer had typed it.
15
+ *
16
+ * ## What is deliberately not stamped
17
+ *
18
+ * `tagType` distinguishes the four kinds of tag in a Vue template
19
+ * (`ElementTypes` in `@vue/compiler-core`):
20
+ *
21
+ * 0 ELEMENT — a real host element. Stamped.
22
+ * 1 COMPONENT — `<MyChild/>`. Not stamped, for the same reason
23
+ * `<PromoField/>` is not stamped in JSX: unless the child
24
+ * happens to land the fallthrough attribute on a DOM node, the
25
+ * attribute goes nowhere. `intrinsicOnly: false` opts in.
26
+ * 2 SLOT — `<slot/>`. A compiler construct, never a DOM node.
27
+ * 3 TEMPLATE — `<template v-if>`. Also never a DOM node.
28
+ *
29
+ * ## Svelte
30
+ *
31
+ * There is deliberately no Svelte equivalent of this file. Svelte 5 already
32
+ * attaches `element.__svelte_meta = { loc: { file, line, column } }` in dev and
33
+ * resolver tier 1 reads it straight off the node (doc 03 §2.2 technique #3,
34
+ * "setup: **zero**"). `.svelte` is therefore absent from the default `include`.
35
+ */
36
+ import { parse as parseSfc } from '@vue/compiler-sfc';
37
+ import path from 'node:path';
38
+ import { MagicString } from '../magic-string.js';
39
+ import { buildAttributes, renderAttributes } from '../stamp.js';
40
+ /* ------------------------------------------------------------------ AST types
41
+ *
42
+ * `@vue/compiler-core` is not a direct dependency of this package (it arrives
43
+ * only as a transitive dependency of `@vue/compiler-sfc`, and its types are not
44
+ * re-exported), so the handful of node shapes this file touches are declared
45
+ * structurally. The numeric constants below are `NodeTypes` / `ElementTypes`
46
+ * from `@vue/compiler-core`, verified against the parser's actual output.
47
+ */
48
+ /** `NodeTypes.ELEMENT` */
49
+ const NODE_TYPE_ELEMENT = 1;
50
+ /** `NodeTypes.ATTRIBUTE` */
51
+ const NODE_TYPE_ATTRIBUTE = 6;
52
+ /** `NodeTypes.DIRECTIVE` */
53
+ const NODE_TYPE_DIRECTIVE = 7;
54
+ /** `ElementTypes.ELEMENT` — a host element, the only kind that reaches the DOM as itself. */
55
+ const ELEMENT_TYPE_ELEMENT = 0;
56
+ /** `ElementTypes.COMPONENT` */
57
+ const ELEMENT_TYPE_COMPONENT = 1;
58
+ /**
59
+ * The component name for an SFC is the file's base name — `App.vue` → `App`.
60
+ *
61
+ * An SFC *is* a component, so unlike JSX there is no enclosing scope to walk;
62
+ * this is the same convention Vue itself uses to infer a component's name when
63
+ * none is declared.
64
+ */
65
+ export function sfcComponentName(file) {
66
+ const base = path.posix.basename(file.replace(/\\/g, '/'));
67
+ const name = base.replace(/\.vue$/i, '');
68
+ return name === '' ? undefined : name;
69
+ }
70
+ function hasAttribute(node, attribute) {
71
+ const props = node.props ?? [];
72
+ return props.some((prop) => {
73
+ if (prop.type === NODE_TYPE_ATTRIBUTE)
74
+ return prop.name === attribute;
75
+ if (prop.type === NODE_TYPE_DIRECTIVE) {
76
+ // `:data-snag="…"` / `v-bind:data-snag="…"` is still a user override.
77
+ return prop.name === 'bind' && prop.arg?.content === attribute;
78
+ }
79
+ return false;
80
+ });
81
+ }
82
+ function shouldStampVue(node, ctx) {
83
+ if (ctx.mode === 'off')
84
+ return false;
85
+ if (node.type !== NODE_TYPE_ELEMENT)
86
+ return false;
87
+ if (typeof node.tag !== 'string' || node.tag === '')
88
+ return false;
89
+ const tagType = node.tagType ?? ELEMENT_TYPE_ELEMENT;
90
+ if (tagType === ELEMENT_TYPE_ELEMENT) {
91
+ // always stamped
92
+ }
93
+ else if (tagType === ELEMENT_TYPE_COMPONENT) {
94
+ if (ctx.intrinsicOnly)
95
+ return false;
96
+ }
97
+ else {
98
+ // SLOT and TEMPLATE never reach the DOM.
99
+ return false;
100
+ }
101
+ if (hasAttribute(node, ctx.attribute))
102
+ return false;
103
+ if (hasAttribute(node, ctx.componentAttribute))
104
+ return false;
105
+ return true;
106
+ }
107
+ /**
108
+ * Insert after the last existing prop, or straight after the tag name when
109
+ * there are none — the same rule the JSX path uses, so a `v-bind="obj"` spread
110
+ * cannot overwrite the stamp.
111
+ */
112
+ function insertionOffset(node) {
113
+ const props = node.props ?? [];
114
+ const last = props.length > 0 ? props[props.length - 1] : undefined;
115
+ if (last !== undefined)
116
+ return last.loc.end.offset;
117
+ return node.loc.start.offset + 1 + (node.tag ?? '').length;
118
+ }
119
+ export function collectVueStampSites(root, ctx) {
120
+ const sites = [];
121
+ const component = sfcComponentName(ctx.file);
122
+ const step = (node) => {
123
+ if (shouldStampVue(node, ctx)) {
124
+ const line = node.loc.start.line;
125
+ // Vue's parser already reports 1-based columns; Babel's are 0-based and
126
+ // get +1 in `babel/plugin.ts`. Both paths emit 1-based.
127
+ const col = node.loc.start.column;
128
+ const attributes = buildAttributes({ line, col, component }, ctx);
129
+ if (attributes.length > 0) {
130
+ sites.push({
131
+ offset: insertionOffset(node),
132
+ line,
133
+ col,
134
+ tag: node.tag ?? '',
135
+ component,
136
+ attributes,
137
+ });
138
+ }
139
+ }
140
+ for (const child of node.children ?? [])
141
+ step(child);
142
+ };
143
+ step(root);
144
+ return sites;
145
+ }
146
+ /**
147
+ * Returns `null` when there is nothing to do — no template, a non-HTML template
148
+ * language, a parse failure, or no stampable element. Like the JSX path, this
149
+ * never throws: a broken dev server costs far more than a missing anchor.
150
+ */
151
+ export function stampVue(code, ctx, filename) {
152
+ if (ctx.mode === 'off')
153
+ return null;
154
+ const name = filename ?? ctx.file;
155
+ let template;
156
+ try {
157
+ const parsed = parseSfc(code, { filename: name });
158
+ template = parsed.descriptor.template;
159
+ }
160
+ catch {
161
+ return null;
162
+ }
163
+ if (template === null || template === undefined)
164
+ return null;
165
+ // `lang="pug"` and friends: the template block is not HTML, so offsets into
166
+ // it would splice attributes into the middle of somebody's indentation.
167
+ if (template.lang !== undefined && template.lang !== '' && template.lang.toLowerCase() !== 'html') {
168
+ return null;
169
+ }
170
+ // `<template src="./other.html">` — nothing of ours lives in this file.
171
+ if (template.src !== undefined && template.src !== '')
172
+ return null;
173
+ if (template.ast === undefined || template.ast === null)
174
+ return null;
175
+ const sites = collectVueStampSites(template.ast, ctx);
176
+ if (sites.length === 0)
177
+ return null;
178
+ const s = new MagicString(code);
179
+ for (const site of sites) {
180
+ s.appendLeft(site.offset, renderAttributes(site.attributes));
181
+ }
182
+ return {
183
+ code: s.toString(),
184
+ map: s.generateMap({ hires: true, source: name, includeContent: false }),
185
+ sites,
186
+ };
187
+ }
188
+ //# sourceMappingURL=transform.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transform.js","sourceRoot":"","sources":["../../src/vue/transform.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ declare const _default: (options?: import("./options.js").AnchorOptions | undefined) => WebpackPluginInstance;
2
+ export default _default;
3
+ //# sourceMappingURL=webpack.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webpack.d.ts","sourceRoot":"","sources":["../src/webpack.ts"],"names":[],"mappings":";AAcA,wBAAoD"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * ```js
3
+ * // webpack.config.js
4
+ * const snagAnchor = require('@frmwrkd/snag-anchor/webpack').default;
5
+ * module.exports = { plugins: [snagAnchor()] };
6
+ * ```
7
+ *
8
+ * Note this is the *plugin*, not the loader. Turbopack's `turbopack.rules` maps
9
+ * extensions to webpack-shaped **loaders**, not to plugins — use
10
+ * `@frmwrkd/snag-anchor/loader` there. See `src/loader.cts`.
11
+ */
12
+ import { createWebpackPlugin } from 'unplugin';
13
+ import { unpluginFactory } from './index.js';
14
+ export default createWebpackPlugin(unpluginFactory);
15
+ //# sourceMappingURL=webpack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webpack.js","sourceRoot":"","sources":["../src/webpack.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,eAAe,mBAAmB,CAAC,eAAe,CAAC,CAAC"}