@designtools/next-plugin 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/loader.js ADDED
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/loader.ts
31
+ var loader_exports = {};
32
+ __export(loader_exports, {
33
+ default: () => designtoolsLoader
34
+ });
35
+ module.exports = __toCommonJS(loader_exports);
36
+ var import_path = __toESM(require("path"));
37
+ function designtoolsLoader(source) {
38
+ const callback = this.async();
39
+ const opts = this.getOptions();
40
+ const cwd = opts.cwd || this.rootContext || process.cwd();
41
+ const relativePath = import_path.default.relative(cwd, this.resourcePath);
42
+ if (relativePath.includes("node_modules")) {
43
+ callback(null, source);
44
+ return;
45
+ }
46
+ if (!source.includes("<")) {
47
+ callback(null, source);
48
+ return;
49
+ }
50
+ try {
51
+ const babel = require("@babel/core");
52
+ const isTsx = this.resourcePath.endsWith(".tsx");
53
+ const result = babel.transformSync(source, {
54
+ filename: this.resourcePath,
55
+ // No presets — we only want to parse and run our visitor, not compile
56
+ presets: [],
57
+ plugins: [
58
+ function designtoolsSourcePlugin() {
59
+ return {
60
+ visitor: {
61
+ JSXOpeningElement(nodePath) {
62
+ const t = babel.types;
63
+ const attrs = nodePath.node.attributes;
64
+ const name = nodePath.node.name;
65
+ if (t.isJSXIdentifier(name) && name.name === "Fragment") return;
66
+ if (t.isJSXMemberExpression(name) && t.isJSXIdentifier(name.property) && name.property.name === "Fragment") return;
67
+ const loc = nodePath.node.loc;
68
+ if (!loc) return;
69
+ const value = `${relativePath}:${loc.start.line}:${loc.start.column}`;
70
+ const isComponent = t.isJSXIdentifier(name) && name.name[0] === name.name[0].toUpperCase() && name.name[0] !== name.name[0].toLowerCase() || t.isJSXMemberExpression(name);
71
+ if (isComponent) {
72
+ const attrName = "data-instance-source";
73
+ if (attrs.some(
74
+ (a) => t.isJSXAttribute(a) && t.isJSXIdentifier(a.name) && a.name.name === attrName
75
+ )) return;
76
+ attrs.push(
77
+ t.jsxAttribute(
78
+ t.jsxIdentifier(attrName),
79
+ t.stringLiteral(value)
80
+ )
81
+ );
82
+ } else {
83
+ if (attrs.some(
84
+ (a) => t.isJSXAttribute(a) && t.isJSXIdentifier(a.name) && a.name.name === "data-source"
85
+ )) return;
86
+ attrs.push(
87
+ t.jsxAttribute(
88
+ t.jsxIdentifier("data-source"),
89
+ t.stringLiteral(value)
90
+ )
91
+ );
92
+ }
93
+ }
94
+ }
95
+ };
96
+ }
97
+ ],
98
+ // Tell Babel's parser to handle JSX and TypeScript syntax
99
+ parserOpts: {
100
+ plugins: ["jsx", ...isTsx ? ["typescript"] : []]
101
+ },
102
+ // Preserve original formatting as much as possible
103
+ retainLines: true,
104
+ // Don't look for user's .babelrc or babel.config — isolation
105
+ configFile: false,
106
+ babelrc: false
107
+ });
108
+ callback(null, result?.code || source);
109
+ } catch (err) {
110
+ console.warn(`[designtools] Source annotation skipped for ${relativePath}: ${err.message}`);
111
+ callback(null, source);
112
+ }
113
+ }
@@ -0,0 +1,86 @@
1
+ import {
2
+ __require
3
+ } from "./chunk-Y6FXYEAI.mjs";
4
+
5
+ // src/loader.ts
6
+ import path from "path";
7
+ function designtoolsLoader(source) {
8
+ const callback = this.async();
9
+ const opts = this.getOptions();
10
+ const cwd = opts.cwd || this.rootContext || process.cwd();
11
+ const relativePath = path.relative(cwd, this.resourcePath);
12
+ if (relativePath.includes("node_modules")) {
13
+ callback(null, source);
14
+ return;
15
+ }
16
+ if (!source.includes("<")) {
17
+ callback(null, source);
18
+ return;
19
+ }
20
+ try {
21
+ const babel = __require("@babel/core");
22
+ const isTsx = this.resourcePath.endsWith(".tsx");
23
+ const result = babel.transformSync(source, {
24
+ filename: this.resourcePath,
25
+ // No presets — we only want to parse and run our visitor, not compile
26
+ presets: [],
27
+ plugins: [
28
+ function designtoolsSourcePlugin() {
29
+ return {
30
+ visitor: {
31
+ JSXOpeningElement(nodePath) {
32
+ const t = babel.types;
33
+ const attrs = nodePath.node.attributes;
34
+ const name = nodePath.node.name;
35
+ if (t.isJSXIdentifier(name) && name.name === "Fragment") return;
36
+ if (t.isJSXMemberExpression(name) && t.isJSXIdentifier(name.property) && name.property.name === "Fragment") return;
37
+ const loc = nodePath.node.loc;
38
+ if (!loc) return;
39
+ const value = `${relativePath}:${loc.start.line}:${loc.start.column}`;
40
+ const isComponent = t.isJSXIdentifier(name) && name.name[0] === name.name[0].toUpperCase() && name.name[0] !== name.name[0].toLowerCase() || t.isJSXMemberExpression(name);
41
+ if (isComponent) {
42
+ const attrName = "data-instance-source";
43
+ if (attrs.some(
44
+ (a) => t.isJSXAttribute(a) && t.isJSXIdentifier(a.name) && a.name.name === attrName
45
+ )) return;
46
+ attrs.push(
47
+ t.jsxAttribute(
48
+ t.jsxIdentifier(attrName),
49
+ t.stringLiteral(value)
50
+ )
51
+ );
52
+ } else {
53
+ if (attrs.some(
54
+ (a) => t.isJSXAttribute(a) && t.isJSXIdentifier(a.name) && a.name.name === "data-source"
55
+ )) return;
56
+ attrs.push(
57
+ t.jsxAttribute(
58
+ t.jsxIdentifier("data-source"),
59
+ t.stringLiteral(value)
60
+ )
61
+ );
62
+ }
63
+ }
64
+ }
65
+ };
66
+ }
67
+ ],
68
+ // Tell Babel's parser to handle JSX and TypeScript syntax
69
+ parserOpts: {
70
+ plugins: ["jsx", ...isTsx ? ["typescript"] : []]
71
+ },
72
+ // Preserve original formatting as much as possible
73
+ retainLines: true,
74
+ // Don't look for user's .babelrc or babel.config — isolation
75
+ configFile: false,
76
+ babelrc: false
77
+ });
78
+ callback(null, result?.code || source);
79
+ } catch (err) {
80
+ console.warn(`[designtools] Source annotation skipped for ${relativePath}: ${err.message}`);
81
+ callback(null, source);
82
+ }
83
+ }
84
+ export {
85
+ designtoolsLoader as default
86
+ };
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "@designtools/next-plugin",
3
+ "version": "0.1.2",
4
+ "main": "dist/index.js",
5
+ "exports": {
6
+ ".": {
7
+ "require": "./dist/index.js",
8
+ "import": "./dist/index.mjs"
9
+ },
10
+ "./codesurface": {
11
+ "require": "./dist/codesurface.js",
12
+ "import": "./dist/codesurface.mjs"
13
+ }
14
+ },
15
+ "scripts": {
16
+ "build": "tsup src/index.ts src/loader.ts src/codesurface.tsx src/codesurface-mount-loader.ts --format cjs,esm --dts --external @babel/core --external react --external react/jsx-runtime"
17
+ },
18
+ "peerDependencies": {
19
+ "next": ">=14.0.0",
20
+ "react": ">=18.0.0"
21
+ },
22
+ "devDependencies": {
23
+ "@types/react": "^19.0.0",
24
+ "next": "^15.0.0",
25
+ "react": "^19.0.0",
26
+ "tsup": "^8.0.0"
27
+ }
28
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Webpack loader that auto-mounts <CodeSurface /> in the root layout.
3
+ * Only runs in development. Injects the import and component into the JSX.
4
+ *
5
+ * Strategy: Simple string injection — find the {children} pattern in the layout
6
+ * and add <CodeSurface /> alongside it.
7
+ */
8
+
9
+ interface LoaderContext {
10
+ resourcePath: string;
11
+ callback(err: Error | null, content?: string): void;
12
+ async(): (err: Error | null, content?: string) => void;
13
+ }
14
+
15
+ export default function codesurfaceMountLoader(this: LoaderContext, source: string): void {
16
+ const callback = this.async();
17
+
18
+ // Only inject into root layout (not nested layouts)
19
+ // Root layout is detected by the presence of <html> tag
20
+ if (!source.includes("<html")) {
21
+ callback(null, source);
22
+ return;
23
+ }
24
+
25
+ // Skip if already has CodeSurface import
26
+ if (source.includes("CodeSurface")) {
27
+ callback(null, source);
28
+ return;
29
+ }
30
+
31
+ // Add import at the top (after "use client" or first import)
32
+ // CodeSurface is a "use client" component — importing it from an RSC is fine in Next.js
33
+ const importStatement = `import { CodeSurface } from "@designtools/next-plugin/codesurface";\n`;
34
+ let modified = source;
35
+
36
+ // Find a good insertion point for the import
37
+ const firstImportIndex = source.indexOf("import ");
38
+ if (firstImportIndex !== -1) {
39
+ modified = source.slice(0, firstImportIndex) + importStatement + source.slice(firstImportIndex);
40
+ } else {
41
+ modified = importStatement + source;
42
+ }
43
+
44
+ // Add <CodeSurface /> just before {children}
45
+ modified = modified.replace(
46
+ /(\{children\})/,
47
+ `<CodeSurface />\n $1`
48
+ );
49
+
50
+ callback(null, modified);
51
+ }