@precedence-dev/instrument 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.
- package/LICENSE +105 -0
- package/README.md +109 -0
- package/bin/precedence-instrument.js +12 -0
- package/dist/discover.d.ts +10 -0
- package/dist/discover.js +95 -0
- package/dist/generate/cli.d.ts +2 -0
- package/dist/generate/cli.js +368 -0
- package/dist/generate/explain.d.ts +21 -0
- package/dist/generate/explain.js +428 -0
- package/dist/generate/instrument.d.ts +165 -0
- package/dist/generate/instrument.js +665 -0
- package/dist/generate/unplugin.d.ts +31 -0
- package/dist/generate/unplugin.js +125 -0
- package/dist/util.d.ts +2 -0
- package/dist/util.js +7 -0
- package/package.json +50 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type Plan, type Skipped } from "./instrument";
|
|
2
|
+
export interface PmInstrumentOptions {
|
|
3
|
+
/** the `copy events` export, a parsed plan, or a path to the JSON file */
|
|
4
|
+
plan: Plan | string;
|
|
5
|
+
/**
|
|
6
|
+
* "direct" (default): inject `track(…)` calls + the delegated-listener module.
|
|
7
|
+
* "runtime": inject `globalThis.__pm?.(id, …)` calls, stamp delegated elements,
|
|
8
|
+
* and let `@precedence-dev/sdk` (installPrecedence) do the rest at runtime.
|
|
9
|
+
* Toggling / renaming an event no longer needs a rebuild.
|
|
10
|
+
*/
|
|
11
|
+
emit?: "direct" | "runtime";
|
|
12
|
+
/** direct mode: "<name> from <module>" adds the import; "<name>" assumes it's global.
|
|
13
|
+
* default "console.log", every event logs "[pm] <name>", no import. */
|
|
14
|
+
track?: string;
|
|
15
|
+
/** direct mode: id for the delegated-listener module (import once at your app root; default "virtual:pm-tracking") */
|
|
16
|
+
virtualModuleId?: string;
|
|
17
|
+
/** per-file callback, wire drift / skips into your build log or fail the build */
|
|
18
|
+
onReport?: (r: {
|
|
19
|
+
file: string;
|
|
20
|
+
applied: number;
|
|
21
|
+
drift: number;
|
|
22
|
+
skipped: Skipped[];
|
|
23
|
+
}) => void;
|
|
24
|
+
}
|
|
25
|
+
export declare const unpluginPmInstrument: import("unplugin").UnpluginInstance<PmInstrumentOptions, boolean>;
|
|
26
|
+
export declare const vitePlugin: (options: PmInstrumentOptions) => any;
|
|
27
|
+
export declare const webpackPlugin: (options: PmInstrumentOptions) => WebpackPluginInstance;
|
|
28
|
+
export declare const rollupPlugin: (options: PmInstrumentOptions) => any;
|
|
29
|
+
export declare const rspackPlugin: (options: PmInstrumentOptions) => RspackPluginInstance;
|
|
30
|
+
export declare const esbuildPlugin: (options: PmInstrumentOptions) => EsbuildPlugin;
|
|
31
|
+
export declare const farmPlugin: (options: PmInstrumentOptions) => JsPlugin;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.farmPlugin = exports.esbuildPlugin = exports.rspackPlugin = exports.rollupPlugin = exports.webpackPlugin = exports.vitePlugin = exports.unpluginPmInstrument = void 0;
|
|
37
|
+
/**
|
|
38
|
+
* precedence-instrument as a bundler transform, the production delivery path.
|
|
39
|
+
*
|
|
40
|
+
* One `transform` hook, adapted to Vite / webpack / Rollup / esbuild / Rspack by
|
|
41
|
+
* unplugin. Source files never change on disk; the `track(…)` calls exist only
|
|
42
|
+
* in the bundle, so instrumentation is idempotent and sourcemap-clean by
|
|
43
|
+
* construction, and CI reviews the plan diff, not a machine-rewritten tree.
|
|
44
|
+
*
|
|
45
|
+
* One implementation, six bundlers: per-module `transform(code, id)`, plan/config
|
|
46
|
+
* as the input.
|
|
47
|
+
*
|
|
48
|
+
* // next.config.mjs
|
|
49
|
+
* import { webpackPlugin as pmInstrument } from "@precedence-dev/instrument/unplugin";
|
|
50
|
+
* const nextConfig = {
|
|
51
|
+
* webpack(config) {
|
|
52
|
+
* config.plugins.push(pmInstrument({
|
|
53
|
+
* plan: "./tracking-plan.json",
|
|
54
|
+
* track: "track from @/lib/analytics",
|
|
55
|
+
* }));
|
|
56
|
+
* return config;
|
|
57
|
+
* },
|
|
58
|
+
* };
|
|
59
|
+
*
|
|
60
|
+
* // vite.config.ts
|
|
61
|
+
* import { vitePlugin as pmInstrument } from "@precedence-dev/instrument/unplugin";
|
|
62
|
+
* export default { plugins: [pmInstrument({ plan, track })] };
|
|
63
|
+
*
|
|
64
|
+
* // app entry, ONCE, pulls in the delegated click listener for synthetic anchors
|
|
65
|
+
* import "virtual:pm-tracking";
|
|
66
|
+
*/
|
|
67
|
+
const fs = __importStar(require("fs"));
|
|
68
|
+
const unplugin_1 = require("unplugin");
|
|
69
|
+
const instrument_1 = require("./instrument");
|
|
70
|
+
const norm = (s) => s.replace(/\\/g, "/").split("?")[0];
|
|
71
|
+
exports.unpluginPmInstrument = (0, unplugin_1.createUnplugin)((options) => {
|
|
72
|
+
const plan = typeof options.plan === "string"
|
|
73
|
+
? JSON.parse(fs.readFileSync(options.plan, "utf8"))
|
|
74
|
+
: options.plan;
|
|
75
|
+
const opts = { track: options.track, emit: options.emit }; // track defaults to console.log in instrument.ts
|
|
76
|
+
const files = (0, instrument_1.planFiles)(plan);
|
|
77
|
+
const delegated = (0, instrument_1.planDelegations)(plan); // from the plan alone, no build ordering dependency
|
|
78
|
+
const VID = options.virtualModuleId || "virtual:pm-tracking";
|
|
79
|
+
const RESOLVED = "\0" + VID;
|
|
80
|
+
const touches = (id) => {
|
|
81
|
+
const c = norm(id);
|
|
82
|
+
return files.some((f) => c === f || c.endsWith("/" + f));
|
|
83
|
+
};
|
|
84
|
+
return {
|
|
85
|
+
name: "unplugin-precedence",
|
|
86
|
+
enforce: "pre", // run before the TS/JSX transpile, like the stamp loader
|
|
87
|
+
transformInclude(id) {
|
|
88
|
+
return touches(id);
|
|
89
|
+
},
|
|
90
|
+
transform(code, id) {
|
|
91
|
+
const r = (0, instrument_1.instrumentFile)(code, id, plan, opts);
|
|
92
|
+
if (!r)
|
|
93
|
+
return null;
|
|
94
|
+
options.onReport?.({ file: r.file, applied: r.applied.length, drift: r.warnings.length, skipped: r.skipped });
|
|
95
|
+
const warn = this.warn;
|
|
96
|
+
r.warnings.forEach((w) => warn?.(`precedence drift: ${w.detail}`));
|
|
97
|
+
r.skipped.forEach((s) => warn?.(`precedence skipped ${s.id ?? ""}: ${s.reason}`));
|
|
98
|
+
if (r.code === code)
|
|
99
|
+
return null; // nothing spliced or stamped
|
|
100
|
+
return r.map ? { code: r.code, map: r.map } : { code: r.code };
|
|
101
|
+
},
|
|
102
|
+
resolveId(id) {
|
|
103
|
+
return id === VID ? RESOLVED : null;
|
|
104
|
+
},
|
|
105
|
+
loadInclude(id) {
|
|
106
|
+
return id === RESOLVED;
|
|
107
|
+
},
|
|
108
|
+
load(id) {
|
|
109
|
+
if (id !== RESOLVED)
|
|
110
|
+
return null;
|
|
111
|
+
if (options.emit === "runtime") {
|
|
112
|
+
return "// runtime mode: import { installPrecedence } from '@precedence-dev/sdk' and call it at your app root instead of importing this\nexport {};\n";
|
|
113
|
+
}
|
|
114
|
+
return delegated.length
|
|
115
|
+
? (0, instrument_1.buildDelegatedModule)(delegated, options.track || "track")
|
|
116
|
+
: "// precedence: no delegated (synthetic) anchors in the plan\nexport {};\n";
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
});
|
|
120
|
+
exports.vitePlugin = exports.unpluginPmInstrument.vite;
|
|
121
|
+
exports.webpackPlugin = exports.unpluginPmInstrument.webpack;
|
|
122
|
+
exports.rollupPlugin = exports.unpluginPmInstrument.rollup;
|
|
123
|
+
exports.rspackPlugin = exports.unpluginPmInstrument.rspack;
|
|
124
|
+
exports.esbuildPlugin = exports.unpluginPmInstrument.esbuild;
|
|
125
|
+
exports.farmPlugin = exports.unpluginPmInstrument.farm;
|
package/dist/util.d.ts
ADDED
package/dist/util.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@precedence-dev/instrument",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Splice tracking calls into source (or a build) from a plan produced by Precedence's analysis engine. Source-available so anyone can read exactly what code it generates and where it puts it.",
|
|
5
|
+
"license": "FSL-1.1-ALv2",
|
|
6
|
+
"author": "Precedence",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/precedence-dev/instrument.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/precedence-dev/instrument",
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public"
|
|
14
|
+
},
|
|
15
|
+
"bin": {
|
|
16
|
+
"precedence-instrument": "bin/precedence-instrument.js"
|
|
17
|
+
},
|
|
18
|
+
"main": "dist/generate/instrument.js",
|
|
19
|
+
"types": "dist/generate/instrument.d.ts",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./dist/generate/instrument.d.ts",
|
|
23
|
+
"default": "./dist/generate/instrument.js"
|
|
24
|
+
},
|
|
25
|
+
"./unplugin": {
|
|
26
|
+
"types": "./dist/generate/unplugin.d.ts",
|
|
27
|
+
"default": "./dist/generate/unplugin.js"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist/",
|
|
32
|
+
"bin/",
|
|
33
|
+
"README.md",
|
|
34
|
+
"LICENSE"
|
|
35
|
+
],
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && tsc",
|
|
38
|
+
"prepare": "npm run build",
|
|
39
|
+
"test": "npm run build && node --test \"test/**/*.test.mjs\" && node test/invariants.mjs"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@precedence-dev/cli": "^0.1.0",
|
|
43
|
+
"magic-string": "^0.30.21",
|
|
44
|
+
"typescript": "^5.6.3",
|
|
45
|
+
"unplugin": "^2.3.11"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@types/node": "^20.14.0"
|
|
49
|
+
}
|
|
50
|
+
}
|