@reckona/mreact-next 0.0.180 → 0.0.182
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/index.d.ts.map +1 -1
- package/dist/index.js +11 -3
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +15 -3
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,+EAA+E;AAC/E,MAAM,WAAW,+BAA+B;IAC9C,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,uFAAuF;AACvF,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,mGAAmG;AACnG,MAAM,WAAW,6BAA6B;IAC5C,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,kGAAkG;AAClG,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,+BAA+B,GACvC,OAAO,CAAC,wBAAwB,EAAE,CAAC,CAkBrC;AAED,sFAAsF;AACtF,MAAM,WAAW,mCAAmC;IAClD,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,2FAA2F;AAC3F,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,mCAAmC,GAC3C,6BAA6B,CA0D/B;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,+EAA+E;AAC/E,MAAM,WAAW,+BAA+B;IAC9C,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,uFAAuF;AACvF,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,mGAAmG;AACnG,MAAM,WAAW,6BAA6B;IAC5C,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,kGAAkG;AAClG,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,+BAA+B,GACvC,OAAO,CAAC,wBAAwB,EAAE,CAAC,CAkBrC;AAED,sFAAsF;AACtF,MAAM,WAAW,mCAAmC;IAClD,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,2FAA2F;AAC3F,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,mCAAmC,GAC3C,6BAA6B,CA0D/B;AAwDD,yEAAyE;AACzE,wBAAgB,+BAA+B,CAC7C,SAAS,EAAE,SAAS,wBAAwB,EAAE,EAC9C,OAAO,EAAE,MAAM,GACd,MAAM,CAWR"}
|
package/dist/index.js
CHANGED
|
@@ -30,7 +30,7 @@ export function compileMreactComponentModule(code, filename, options) {
|
|
|
30
30
|
throw new Error(`${filename} must export at least one mreact JSX component.`);
|
|
31
31
|
}
|
|
32
32
|
const components = compiled.metadata.components
|
|
33
|
-
.filter((component) =>
|
|
33
|
+
.filter((component) => hasSourceExport(code, component.name, component.exportName))
|
|
34
34
|
.map((component) => ({
|
|
35
35
|
name: component.name,
|
|
36
36
|
exportName: component.exportName,
|
|
@@ -99,8 +99,16 @@ function importPathForGeneratedModule(from, to) {
|
|
|
99
99
|
function emitExportFunction(exportName, name) {
|
|
100
100
|
return exportName === "default" ? `export default function ${name}` : `export function ${name}`;
|
|
101
101
|
}
|
|
102
|
-
function
|
|
103
|
-
|
|
102
|
+
function hasSourceExport(code, name, exportName) {
|
|
103
|
+
if (exportName === "default") {
|
|
104
|
+
return /\bexport\s+default\b/.test(code);
|
|
105
|
+
}
|
|
106
|
+
return (new RegExp(`\\bexport\\s+(?:async\\s+)?function\\s+${escapeRegExp(name)}\\b`).test(code) ||
|
|
107
|
+
new RegExp(`\\bexport\\s+(?:const|let|var)\\s+${escapeRegExp(name)}\\b`).test(code) ||
|
|
108
|
+
new RegExp(`\\bexport\\s*\\{[^}]*\\b${escapeRegExp(exportName)}\\b[^}]*\\}`).test(code));
|
|
109
|
+
}
|
|
110
|
+
function escapeRegExp(value) {
|
|
111
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
104
112
|
}
|
|
105
113
|
/** Formats generated component paths as a human-readable CLI summary. */
|
|
106
114
|
export function formatGeneratedMreactComponents(generated, rootDir) {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { readdir, readFile, writeFile } from \"node:fs/promises\";\nimport { basename, dirname, join, relative } from \"node:path\";\nimport { transform } from \"@reckona/mreact-compiler\";\n\n/** Configures the root directory scanned for `.mreact.tsx` component files. */\nexport interface GenerateMreactComponentsOptions {\n rootDir: string;\n}\n\n/** Describes one source component and the wrapper and DOM modules generated for it. */\nexport interface GeneratedMreactComponent {\n source: string;\n output: string;\n domOutput: string;\n}\n\n/** Contains the generated wrapper module code and browser DOM module code for a component file. */\nexport interface CompiledMreactComponentModule {\n wrapperCode: string;\n domCode: string;\n}\n\n/** Generates wrapper and DOM modules for every `.mreact.tsx` component under a root directory. */\nexport async function generateMreactComponents(\n options: GenerateMreactComponentsOptions,\n): Promise<GeneratedMreactComponent[]> {\n const sources = await findMreactSources(options.rootDir);\n const generated: GeneratedMreactComponent[] = [];\n\n for (const source of sources) {\n const code = await readFile(source, \"utf8\");\n const output = outputPathForSource(source);\n const domOutput = domOutputPathForSource(source);\n const generatedCode = compileMreactComponentModule(code, source, {\n domImportPath: importPathForGeneratedModule(output, domOutput),\n });\n\n await writeFile(output, generatedCode.wrapperCode);\n await writeFile(domOutput, generatedCode.domCode);\n generated.push({ source, output, domOutput });\n }\n\n return generated;\n}\n\n/** Configures the import path from the wrapper module to the generated DOM module. */\nexport interface CompileMreactComponentModuleOptions {\n domImportPath: string;\n}\n\n/** Compiles one `.mreact.tsx` component module into wrapper and DOM module source code. */\nexport function compileMreactComponentModule(\n code: string,\n filename: string,\n options: CompileMreactComponentModuleOptions,\n): CompiledMreactComponentModule {\n const compiled = transform({\n code,\n filename,\n target: \"client\",\n dev: true,\n });\n\n if (compiled.metadata.components.length === 0) {\n throw new Error(`${filename} must export at least one mreact JSX component.`);\n }\n\n const components = compiled.metadata.components\n .filter((component) => hasCompiledExport(compiled.code, component.name, component.exportName))\n .map((component) => ({\n name: component.name,\n exportName: component.exportName,\n moduleAccess:\n component.exportName === \"default\" ? \"module.default\" : `module.${component.name}`,\n }));\n\n if (components.length === 0) {\n throw new Error(`${filename} must export at least one mreact JSX component.`);\n }\n const wrappers = components\n .map(\n (component) => `const ${component.name}$mounted = new WeakMap<Element, Node>();\nconst ${component.name}$mounting = new WeakSet<Element>();\n${emitExportFunction(component.exportName, component.name)}(props: Record<string, unknown>): never {\n return (\n <span\n data-mreact-component=${JSON.stringify(component.name)}\n ref={(node: Element | null) => {\n if (node === null || ${component.name}$mounted.has(node) || ${component.name}$mounting.has(node)) {\n return;\n }\n\n ${component.name}$mounting.add(node);\n void import(${JSON.stringify(options.domImportPath)}).then((module) => {\n const rendered = (${component.moduleAccess} as (props: Record<string, unknown>) => Node)(props);\n node.replaceChildren(rendered);\n ${component.name}$mounted.set(node, rendered);\n });\n }}\n />\n ) as never;\n}`,\n )\n .join(\"\\n\\n\");\n\n return {\n wrapperCode: `// @ts-nocheck\n\"use client\";\n${wrappers}\n`,\n domCode: `// @ts-nocheck\n${compiled.code}`,\n };\n}\n\nasync function findMreactSources(rootDir: string): Promise<string[]> {\n const entries = await readdir(rootDir, { withFileTypes: true });\n const sources: string[] = [];\n\n for (const entry of entries) {\n const path = join(rootDir, entry.name);\n\n if (entry.isDirectory()) {\n sources.push(...(await findMreactSources(path)));\n continue;\n }\n\n if (entry.isFile() && entry.name.endsWith(\".mreact.tsx\")) {\n sources.push(path);\n }\n }\n\n return sources.sort();\n}\n\nfunction outputPathForSource(source: string): string {\n return join(dirname(source), `${basename(source, \".mreact.tsx\")}.tsx`);\n}\n\nfunction domOutputPathForSource(source: string): string {\n return join(dirname(source), `${basename(source, \".mreact.tsx\")}.mreact-dom.ts`);\n}\n\nfunction importPathForGeneratedModule(from: string, to: string): string {\n const extensionless = relative(dirname(from), to).replace(/\\.ts$/, \"\");\n\n return extensionless.startsWith(\".\") ? extensionless : `./${extensionless}`;\n}\n\nfunction emitExportFunction(exportName: string, name: string): string {\n return exportName === \"default\" ? `export default function ${name}` : `export function ${name}`;\n}\n\nfunction hasCompiledExport(code: string, name: string, exportName: string): boolean {\n return code.includes(`${emitExportFunction(exportName, name)}(`);\n}\n\n/** Formats generated component paths as a human-readable CLI summary. */\nexport function formatGeneratedMreactComponents(\n generated: readonly GeneratedMreactComponent[],\n rootDir: string,\n): string {\n if (generated.length === 0) {\n return \"No .mreact.tsx components found.\";\n }\n\n return generated\n .map(\n (item) =>\n `${relative(rootDir, item.source)} -> ${relative(rootDir, item.output)}, ${relative(rootDir, item.domOutput)}`,\n )\n .join(\"\\n\");\n}\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { readdir, readFile, writeFile } from \"node:fs/promises\";\nimport { basename, dirname, join, relative } from \"node:path\";\nimport { transform } from \"@reckona/mreact-compiler\";\n\n/** Configures the root directory scanned for `.mreact.tsx` component files. */\nexport interface GenerateMreactComponentsOptions {\n rootDir: string;\n}\n\n/** Describes one source component and the wrapper and DOM modules generated for it. */\nexport interface GeneratedMreactComponent {\n source: string;\n output: string;\n domOutput: string;\n}\n\n/** Contains the generated wrapper module code and browser DOM module code for a component file. */\nexport interface CompiledMreactComponentModule {\n wrapperCode: string;\n domCode: string;\n}\n\n/** Generates wrapper and DOM modules for every `.mreact.tsx` component under a root directory. */\nexport async function generateMreactComponents(\n options: GenerateMreactComponentsOptions,\n): Promise<GeneratedMreactComponent[]> {\n const sources = await findMreactSources(options.rootDir);\n const generated: GeneratedMreactComponent[] = [];\n\n for (const source of sources) {\n const code = await readFile(source, \"utf8\");\n const output = outputPathForSource(source);\n const domOutput = domOutputPathForSource(source);\n const generatedCode = compileMreactComponentModule(code, source, {\n domImportPath: importPathForGeneratedModule(output, domOutput),\n });\n\n await writeFile(output, generatedCode.wrapperCode);\n await writeFile(domOutput, generatedCode.domCode);\n generated.push({ source, output, domOutput });\n }\n\n return generated;\n}\n\n/** Configures the import path from the wrapper module to the generated DOM module. */\nexport interface CompileMreactComponentModuleOptions {\n domImportPath: string;\n}\n\n/** Compiles one `.mreact.tsx` component module into wrapper and DOM module source code. */\nexport function compileMreactComponentModule(\n code: string,\n filename: string,\n options: CompileMreactComponentModuleOptions,\n): CompiledMreactComponentModule {\n const compiled = transform({\n code,\n filename,\n target: \"client\",\n dev: true,\n });\n\n if (compiled.metadata.components.length === 0) {\n throw new Error(`${filename} must export at least one mreact JSX component.`);\n }\n\n const components = compiled.metadata.components\n .filter((component) => hasSourceExport(code, component.name, component.exportName))\n .map((component) => ({\n name: component.name,\n exportName: component.exportName,\n moduleAccess:\n component.exportName === \"default\" ? \"module.default\" : `module.${component.name}`,\n }));\n\n if (components.length === 0) {\n throw new Error(`${filename} must export at least one mreact JSX component.`);\n }\n const wrappers = components\n .map(\n (component) => `const ${component.name}$mounted = new WeakMap<Element, Node>();\nconst ${component.name}$mounting = new WeakSet<Element>();\n${emitExportFunction(component.exportName, component.name)}(props: Record<string, unknown>): never {\n return (\n <span\n data-mreact-component=${JSON.stringify(component.name)}\n ref={(node: Element | null) => {\n if (node === null || ${component.name}$mounted.has(node) || ${component.name}$mounting.has(node)) {\n return;\n }\n\n ${component.name}$mounting.add(node);\n void import(${JSON.stringify(options.domImportPath)}).then((module) => {\n const rendered = (${component.moduleAccess} as (props: Record<string, unknown>) => Node)(props);\n node.replaceChildren(rendered);\n ${component.name}$mounted.set(node, rendered);\n });\n }}\n />\n ) as never;\n}`,\n )\n .join(\"\\n\\n\");\n\n return {\n wrapperCode: `// @ts-nocheck\n\"use client\";\n${wrappers}\n`,\n domCode: `// @ts-nocheck\n${compiled.code}`,\n };\n}\n\nasync function findMreactSources(rootDir: string): Promise<string[]> {\n const entries = await readdir(rootDir, { withFileTypes: true });\n const sources: string[] = [];\n\n for (const entry of entries) {\n const path = join(rootDir, entry.name);\n\n if (entry.isDirectory()) {\n sources.push(...(await findMreactSources(path)));\n continue;\n }\n\n if (entry.isFile() && entry.name.endsWith(\".mreact.tsx\")) {\n sources.push(path);\n }\n }\n\n return sources.sort();\n}\n\nfunction outputPathForSource(source: string): string {\n return join(dirname(source), `${basename(source, \".mreact.tsx\")}.tsx`);\n}\n\nfunction domOutputPathForSource(source: string): string {\n return join(dirname(source), `${basename(source, \".mreact.tsx\")}.mreact-dom.ts`);\n}\n\nfunction importPathForGeneratedModule(from: string, to: string): string {\n const extensionless = relative(dirname(from), to).replace(/\\.ts$/, \"\");\n\n return extensionless.startsWith(\".\") ? extensionless : `./${extensionless}`;\n}\n\nfunction emitExportFunction(exportName: string, name: string): string {\n return exportName === \"default\" ? `export default function ${name}` : `export function ${name}`;\n}\n\nfunction hasSourceExport(code: string, name: string, exportName: string): boolean {\n if (exportName === \"default\") {\n return /\\bexport\\s+default\\b/.test(code);\n }\n\n return (\n new RegExp(`\\\\bexport\\\\s+(?:async\\\\s+)?function\\\\s+${escapeRegExp(name)}\\\\b`).test(code) ||\n new RegExp(`\\\\bexport\\\\s+(?:const|let|var)\\\\s+${escapeRegExp(name)}\\\\b`).test(code) ||\n new RegExp(`\\\\bexport\\\\s*\\\\{[^}]*\\\\b${escapeRegExp(exportName)}\\\\b[^}]*\\\\}`).test(code)\n );\n}\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n/** Formats generated component paths as a human-readable CLI summary. */\nexport function formatGeneratedMreactComponents(\n generated: readonly GeneratedMreactComponent[],\n rootDir: string,\n): string {\n if (generated.length === 0) {\n return \"No .mreact.tsx components found.\";\n }\n\n return generated\n .map(\n (item) =>\n `${relative(rootDir, item.source)} -> ${relative(rootDir, item.output)}, ${relative(rootDir, item.domOutput)}`,\n )\n .join(\"\\n\");\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reckona/mreact-next",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.182",
|
|
4
4
|
"description": "Experimental helpers for consuming compiled mreact components from Next.js.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"compiler",
|
|
@@ -40,6 +40,6 @@
|
|
|
40
40
|
"access": "public"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@reckona/mreact-compiler": "0.0.
|
|
43
|
+
"@reckona/mreact-compiler": "0.0.182"
|
|
44
44
|
}
|
|
45
45
|
}
|
package/src/index.ts
CHANGED
|
@@ -66,7 +66,7 @@ export function compileMreactComponentModule(
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
const components = compiled.metadata.components
|
|
69
|
-
.filter((component) =>
|
|
69
|
+
.filter((component) => hasSourceExport(code, component.name, component.exportName))
|
|
70
70
|
.map((component) => ({
|
|
71
71
|
name: component.name,
|
|
72
72
|
exportName: component.exportName,
|
|
@@ -151,8 +151,20 @@ function emitExportFunction(exportName: string, name: string): string {
|
|
|
151
151
|
return exportName === "default" ? `export default function ${name}` : `export function ${name}`;
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
-
function
|
|
155
|
-
|
|
154
|
+
function hasSourceExport(code: string, name: string, exportName: string): boolean {
|
|
155
|
+
if (exportName === "default") {
|
|
156
|
+
return /\bexport\s+default\b/.test(code);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return (
|
|
160
|
+
new RegExp(`\\bexport\\s+(?:async\\s+)?function\\s+${escapeRegExp(name)}\\b`).test(code) ||
|
|
161
|
+
new RegExp(`\\bexport\\s+(?:const|let|var)\\s+${escapeRegExp(name)}\\b`).test(code) ||
|
|
162
|
+
new RegExp(`\\bexport\\s*\\{[^}]*\\b${escapeRegExp(exportName)}\\b[^}]*\\}`).test(code)
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function escapeRegExp(value: string): string {
|
|
167
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
156
168
|
}
|
|
157
169
|
|
|
158
170
|
/** Formats generated component paths as a human-readable CLI summary. */
|