@reckona/mreact-next 0.0.159 → 0.0.161

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 CHANGED
@@ -1,19 +1,26 @@
1
+ /** Configures the root directory scanned for `.mreact.tsx` component files. */
1
2
  export interface GenerateMreactComponentsOptions {
2
3
  rootDir: string;
3
4
  }
5
+ /** Describes one source component and the wrapper and DOM modules generated for it. */
4
6
  export interface GeneratedMreactComponent {
5
7
  source: string;
6
8
  output: string;
7
9
  domOutput: string;
8
10
  }
11
+ /** Contains the generated wrapper module code and browser DOM module code for a component file. */
9
12
  export interface CompiledMreactComponentModule {
10
13
  wrapperCode: string;
11
14
  domCode: string;
12
15
  }
16
+ /** Generates wrapper and DOM modules for every `.mreact.tsx` component under a root directory. */
13
17
  export declare function generateMreactComponents(options: GenerateMreactComponentsOptions): Promise<GeneratedMreactComponent[]>;
18
+ /** Configures the import path from the wrapper module to the generated DOM module. */
14
19
  export interface CompileMreactComponentModuleOptions {
15
20
  domImportPath: string;
16
21
  }
22
+ /** Compiles one `.mreact.tsx` component module into wrapper and DOM module source code. */
17
23
  export declare function compileMreactComponentModule(code: string, filename: string, options: CompileMreactComponentModuleOptions): CompiledMreactComponentModule;
24
+ /** Formats generated component paths as a human-readable CLI summary. */
18
25
  export declare function formatGeneratedMreactComponents(generated: readonly GeneratedMreactComponent[], rootDir: string): string;
19
26
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,+BAA+B;IAC9C,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,6BAA6B;IAC5C,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,+BAA+B,GACvC,OAAO,CAAC,wBAAwB,EAAE,CAAC,CAkBrC;AAED,MAAM,WAAW,mCAAmC;IAClD,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,mCAAmC,GAC3C,6BAA6B,CA0D/B;AA4CD,wBAAgB,+BAA+B,CAC7C,SAAS,EAAE,SAAS,wBAAwB,EAAE,EAC9C,OAAO,EAAE,MAAM,GACd,MAAM,CAWR"}
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;AA4CD,yEAAyE;AACzE,wBAAgB,+BAA+B,CAC7C,SAAS,EAAE,SAAS,wBAAwB,EAAE,EAC9C,OAAO,EAAE,MAAM,GACd,MAAM,CAWR"}
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { readdir, readFile, writeFile } from "node:fs/promises";
2
2
  import { basename, dirname, join, relative } from "node:path";
3
3
  import { transform } from "@reckona/mreact-compiler";
4
+ /** Generates wrapper and DOM modules for every `.mreact.tsx` component under a root directory. */
4
5
  export async function generateMreactComponents(options) {
5
6
  const sources = await findMreactSources(options.rootDir);
6
7
  const generated = [];
@@ -17,6 +18,7 @@ export async function generateMreactComponents(options) {
17
18
  }
18
19
  return generated;
19
20
  }
21
+ /** Compiles one `.mreact.tsx` component module into wrapper and DOM module source code. */
20
22
  export function compileMreactComponentModule(code, filename, options) {
21
23
  const compiled = transform({
22
24
  code,
@@ -100,6 +102,7 @@ function emitExportFunction(exportName, name) {
100
102
  function hasCompiledExport(code, name, exportName) {
101
103
  return code.includes(`${emitExportFunction(exportName, name)}(`);
102
104
  }
105
+ /** Formats generated component paths as a human-readable CLI summary. */
103
106
  export function formatGeneratedMreactComponents(generated, rootDir) {
104
107
  if (generated.length === 0) {
105
108
  return "No .mreact.tsx components found.";
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\nexport interface GenerateMreactComponentsOptions {\n rootDir: string;\n}\n\nexport interface GeneratedMreactComponent {\n source: string;\n output: string;\n domOutput: string;\n}\n\nexport interface CompiledMreactComponentModule {\n wrapperCode: string;\n domCode: string;\n}\n\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\nexport interface CompileMreactComponentModuleOptions {\n domImportPath: string;\n}\n\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\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) => 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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reckona/mreact-next",
3
- "version": "0.0.159",
3
+ "version": "0.0.161",
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.159"
43
+ "@reckona/mreact-compiler": "0.0.161"
44
44
  }
45
45
  }
package/src/index.ts CHANGED
@@ -2,21 +2,25 @@ import { readdir, readFile, writeFile } from "node:fs/promises";
2
2
  import { basename, dirname, join, relative } from "node:path";
3
3
  import { transform } from "@reckona/mreact-compiler";
4
4
 
5
+ /** Configures the root directory scanned for `.mreact.tsx` component files. */
5
6
  export interface GenerateMreactComponentsOptions {
6
7
  rootDir: string;
7
8
  }
8
9
 
10
+ /** Describes one source component and the wrapper and DOM modules generated for it. */
9
11
  export interface GeneratedMreactComponent {
10
12
  source: string;
11
13
  output: string;
12
14
  domOutput: string;
13
15
  }
14
16
 
17
+ /** Contains the generated wrapper module code and browser DOM module code for a component file. */
15
18
  export interface CompiledMreactComponentModule {
16
19
  wrapperCode: string;
17
20
  domCode: string;
18
21
  }
19
22
 
23
+ /** Generates wrapper and DOM modules for every `.mreact.tsx` component under a root directory. */
20
24
  export async function generateMreactComponents(
21
25
  options: GenerateMreactComponentsOptions,
22
26
  ): Promise<GeneratedMreactComponent[]> {
@@ -39,10 +43,12 @@ export async function generateMreactComponents(
39
43
  return generated;
40
44
  }
41
45
 
46
+ /** Configures the import path from the wrapper module to the generated DOM module. */
42
47
  export interface CompileMreactComponentModuleOptions {
43
48
  domImportPath: string;
44
49
  }
45
50
 
51
+ /** Compiles one `.mreact.tsx` component module into wrapper and DOM module source code. */
46
52
  export function compileMreactComponentModule(
47
53
  code: string,
48
54
  filename: string,
@@ -149,6 +155,7 @@ function hasCompiledExport(code: string, name: string, exportName: string): bool
149
155
  return code.includes(`${emitExportFunction(exportName, name)}(`);
150
156
  }
151
157
 
158
+ /** Formats generated component paths as a human-readable CLI summary. */
152
159
  export function formatGeneratedMreactComponents(
153
160
  generated: readonly GeneratedMreactComponent[],
154
161
  rootDir: string,