@limno/core 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.md +110 -0
- package/dist/index.cjs +543 -0
- package/dist/index.d.cts +74 -0
- package/dist/index.d.ts +74 -0
- package/dist/index.js +494 -0
- package/dist/loader.cjs +133 -0
- package/package.json +54 -0
package/dist/loader.cjs
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
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: () => limnoLoader
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(loader_exports);
|
|
36
|
+
var import_node_path = __toESM(require("path"), 1);
|
|
37
|
+
|
|
38
|
+
// src/files.ts
|
|
39
|
+
var SOURCE_FILES = /\.(jsx|tsx|js|mjs)$/;
|
|
40
|
+
function shouldStamp(file, include = SOURCE_FILES) {
|
|
41
|
+
return include.test(file) && !/[\\/]node_modules[\\/]/.test(file) && !file.startsWith("\0");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// src/transform.ts
|
|
45
|
+
var import_parser = require("@babel/parser");
|
|
46
|
+
var import_traverse = __toESM(require("@babel/traverse"), 1);
|
|
47
|
+
var import_magic_string = __toESM(require("magic-string"), 1);
|
|
48
|
+
var traverse = import_traverse.default.default ?? import_traverse.default;
|
|
49
|
+
var SKIP_COMPOSITES = /* @__PURE__ */ new Set(["Fragment", "React.Fragment", "Suspense", "React.Suspense", "StrictMode", "React.StrictMode", "Profiler", "React.Profiler", "Activity"]);
|
|
50
|
+
function jsxName(name) {
|
|
51
|
+
if (name.type === "JSXIdentifier") return name.name;
|
|
52
|
+
if (name.type === "JSXNamespacedName") return `${name.namespace.name}:${name.name.name}`;
|
|
53
|
+
return `${jsxName(name.object)}.${name.property.name}`;
|
|
54
|
+
}
|
|
55
|
+
var isCapitalized = (s) => /^[A-Z]/.test(s);
|
|
56
|
+
function componentName(path2, fileBase) {
|
|
57
|
+
let p = path2.getFunctionParent();
|
|
58
|
+
while (p) {
|
|
59
|
+
const node = p.node;
|
|
60
|
+
let name;
|
|
61
|
+
if ((node.type === "FunctionDeclaration" || node.type === "FunctionExpression") && node.id) name = node.id.name;
|
|
62
|
+
if (!name) {
|
|
63
|
+
let parent = p.parentPath;
|
|
64
|
+
while (parent && parent.node.type === "CallExpression") parent = parent.parentPath;
|
|
65
|
+
if (parent?.node.type === "VariableDeclarator" && parent.node.id.type === "Identifier") name = parent.node.id.name;
|
|
66
|
+
else if (parent?.node.type === "ExportDefaultDeclaration") name = fileBase;
|
|
67
|
+
else if (parent?.node.type === "ClassMethod" || parent?.node.type === "ObjectMethod") name = void 0;
|
|
68
|
+
}
|
|
69
|
+
if (!name && p.node.type === "ClassMethod") {
|
|
70
|
+
const cls = p.findParent((q) => q.isClassDeclaration());
|
|
71
|
+
name = cls?.node.id?.name;
|
|
72
|
+
}
|
|
73
|
+
if (name && isCapitalized(name)) return name;
|
|
74
|
+
p = p.parentPath ? p.parentPath.getFunctionParent() : null;
|
|
75
|
+
}
|
|
76
|
+
return void 0;
|
|
77
|
+
}
|
|
78
|
+
function hasAttr(node, name) {
|
|
79
|
+
return node.attributes.some((a) => a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && a.name.name === name);
|
|
80
|
+
}
|
|
81
|
+
var escapeAttr = (s) => s.replace(/&/g, "&").replace(/"/g, """);
|
|
82
|
+
function stampSource(code, id, relFile) {
|
|
83
|
+
if (!code.includes("<")) return null;
|
|
84
|
+
const isTs = /\.tsx$/.test(id);
|
|
85
|
+
let ast;
|
|
86
|
+
try {
|
|
87
|
+
ast = (0, import_parser.parse)(code, {
|
|
88
|
+
sourceType: "module",
|
|
89
|
+
plugins: isTs ? ["jsx", "typescript"] : ["jsx"],
|
|
90
|
+
errorRecovery: true
|
|
91
|
+
});
|
|
92
|
+
} catch {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
const s = new import_magic_string.default(code);
|
|
96
|
+
const fileBase = relFile.split("/").pop().replace(/\.[jt]sx?$/, "").replace(/^./, (c) => c.toUpperCase());
|
|
97
|
+
let touched = false;
|
|
98
|
+
traverse(ast, {
|
|
99
|
+
JSXOpeningElement(path2) {
|
|
100
|
+
const node = path2.node;
|
|
101
|
+
const loc = node.loc;
|
|
102
|
+
if (!loc || node.name.end == null) return;
|
|
103
|
+
const name = jsxName(node.name);
|
|
104
|
+
const where = `${relFile}:${loc.start.line}:${loc.start.column + 1}`;
|
|
105
|
+
const host = node.name.type === "JSXIdentifier" && !isCapitalized(name);
|
|
106
|
+
if (host) {
|
|
107
|
+
if (hasAttr(node, "data-limno-src")) return;
|
|
108
|
+
const comp = componentName(path2, fileBase);
|
|
109
|
+
s.appendLeft(node.name.end, ` data-limno-src="${escapeAttr(where)}"${comp ? ` data-limno-comp="${escapeAttr(comp)}"` : ""}`);
|
|
110
|
+
touched = true;
|
|
111
|
+
} else {
|
|
112
|
+
if (SKIP_COMPOSITES.has(name) || /\.(Provider|Consumer)$/.test(name) || hasAttr(node, "data-limno-callsite")) return;
|
|
113
|
+
s.appendLeft(node.name.end, ` data-limno-callsite="${escapeAttr(where)}"`);
|
|
114
|
+
touched = true;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
if (!touched) return null;
|
|
119
|
+
return { code: s.toString(), map: s.generateMap({ hires: "boundary", source: id, includeContent: true }) };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// src/loader.ts
|
|
123
|
+
function limnoLoader(source, inputMap) {
|
|
124
|
+
this.cacheable?.(true);
|
|
125
|
+
const opts = this.getOptions?.() ?? (typeof this.query === "object" ? this.query : {}) ?? {};
|
|
126
|
+
const root = opts.root ?? process.cwd();
|
|
127
|
+
const file = this.resourcePath;
|
|
128
|
+
if (!shouldStamp(file)) return this.callback(null, source, inputMap);
|
|
129
|
+
const rel = import_node_path.default.relative(root, file).split(import_node_path.default.sep).join("/");
|
|
130
|
+
const out = stampSource(source, file, rel);
|
|
131
|
+
if (!out) return this.callback(null, source, inputMap);
|
|
132
|
+
this.callback(null, out.code, out.map);
|
|
133
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@limno/core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Limno's framework-neutral dev tooling: JSX source stamping, the change-set store and the endpoints the editor talks to.",
|
|
5
|
+
"license": "FSL-1.1-MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.cjs",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
},
|
|
15
|
+
"./loader": "./dist/loader.cjs"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/armie3/limno.git",
|
|
23
|
+
"directory": "packages/core"
|
|
24
|
+
},
|
|
25
|
+
"homepage": "https://github.com/armie3/limno#readme",
|
|
26
|
+
"keywords": [
|
|
27
|
+
"limno",
|
|
28
|
+
"design",
|
|
29
|
+
"visual-editing",
|
|
30
|
+
"coding-agent",
|
|
31
|
+
"mcp"
|
|
32
|
+
],
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=20"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "tsup src/index.ts --format esm,cjs --dts --shims --clean && tsup src/loader.ts --format cjs --out-dir dist --no-clean"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@babel/parser": "^7.29.0",
|
|
44
|
+
"@babel/traverse": "^7.29.0",
|
|
45
|
+
"@limno/mcp": "0.1.0",
|
|
46
|
+
"@limno/overlay": "0.1.0",
|
|
47
|
+
"@limno/spec": "0.1.0",
|
|
48
|
+
"launch-editor": "^2.14.1",
|
|
49
|
+
"magic-string": "^0.30.17"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@types/babel__traverse": "^7.20.7"
|
|
53
|
+
}
|
|
54
|
+
}
|