@mayson-org/inject-script 1.0.0 → 1.0.3

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.
Files changed (58) hide show
  1. package/.env.example +9 -0
  2. package/README.md +57 -45
  3. package/bin/cli.mjs +1 -158
  4. package/dist/cli.d.ts +3 -0
  5. package/dist/cli.d.ts.map +1 -0
  6. package/dist/cli.js +104 -0
  7. package/dist/core/find-root-layout.d.ts +2 -0
  8. package/dist/core/find-root-layout.d.ts.map +1 -0
  9. package/dist/core/find-root-layout.js +61 -0
  10. package/dist/core/generators.d.ts +4 -0
  11. package/dist/core/generators.d.ts.map +1 -0
  12. package/dist/core/generators.js +37 -0
  13. package/dist/core/injector.d.ts +4 -0
  14. package/dist/core/injector.d.ts.map +1 -0
  15. package/dist/core/injector.js +76 -0
  16. package/dist/core/types.d.ts +48 -0
  17. package/dist/core/types.d.ts.map +1 -0
  18. package/dist/core/types.js +1 -0
  19. package/dist/find-root-layout.d.ts +0 -3
  20. package/dist/find-root-layout.d.ts.map +1 -1
  21. package/dist/find-root-layout.js +1 -15
  22. package/dist/generators.d.ts +13 -0
  23. package/dist/generators.d.ts.map +1 -0
  24. package/dist/generators.js +134 -0
  25. package/dist/index.d.ts +14 -6
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +65 -9
  28. package/dist/injector.d.ts +5 -3
  29. package/dist/injector.d.ts.map +1 -1
  30. package/dist/injector.js +67 -61
  31. package/dist/plugins/ascii/index.d.ts +3 -0
  32. package/dist/plugins/ascii/index.d.ts.map +1 -0
  33. package/dist/plugins/ascii/index.js +25 -0
  34. package/dist/plugins/index.d.ts +7 -0
  35. package/dist/plugins/index.d.ts.map +1 -0
  36. package/dist/plugins/index.js +9 -0
  37. package/dist/plugins/metadata/index.d.ts +3 -0
  38. package/dist/plugins/metadata/index.d.ts.map +1 -0
  39. package/dist/plugins/metadata/index.js +14 -0
  40. package/dist/plugins/watermark/index.d.ts +4 -0
  41. package/dist/plugins/watermark/index.d.ts.map +1 -0
  42. package/dist/plugins/watermark/index.js +7 -0
  43. package/dist/plugins/watermark/template.d.ts +9 -0
  44. package/dist/plugins/watermark/template.d.ts.map +1 -0
  45. package/dist/plugins/watermark/template.js +128 -0
  46. package/dist/shared/config.d.ts +13 -0
  47. package/dist/shared/config.d.ts.map +1 -0
  48. package/dist/shared/config.js +25 -0
  49. package/dist/shared/generated-app-metadata.d.ts +28 -0
  50. package/dist/shared/generated-app-metadata.d.ts.map +1 -0
  51. package/dist/shared/generated-app-metadata.js +103 -0
  52. package/dist/shared/remix-url.d.ts +13 -0
  53. package/dist/shared/remix-url.d.ts.map +1 -0
  54. package/dist/shared/remix-url.js +16 -0
  55. package/package.json +15 -8
  56. package/src/find-root-layout.ts +0 -88
  57. package/src/index.ts +0 -27
  58. package/src/injector.ts +0 -101
@@ -0,0 +1 @@
1
+ export {};
@@ -1,5 +1,2 @@
1
- /**
2
- * Finds the most likely Next.js App Router root layout file in a project.
3
- */
4
1
  export declare function findRootLayout(projectRoot?: string): string | null;
5
2
  //# sourceMappingURL=find-root-layout.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"find-root-layout.d.ts","sourceRoot":"","sources":["../src/find-root-layout.ts"],"names":[],"mappings":"AAyDA;;GAEG;AACH,wBAAgB,cAAc,CAAC,WAAW,GAAE,MAAsB,GAAG,MAAM,GAAG,IAAI,CA2BjF"}
1
+ {"version":3,"file":"find-root-layout.d.ts","sourceRoot":"","sources":["../src/find-root-layout.ts"],"names":[],"mappings":"AAgDA,wBAAgB,cAAc,CAAC,WAAW,GAAE,MAAsB,GAAG,MAAM,GAAG,IAAI,CA0BjF"}
@@ -1,9 +1,6 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
3
  const LAYOUT_EXTENSIONS = ['.tsx', '.jsx', '.ts', '.js'];
4
- /**
5
- * Recursively search a directory for layout files.
6
- */
7
4
  function findLayoutFiles(dir, fileList = []) {
8
5
  if (!fs.existsSync(dir))
9
6
  return fileList;
@@ -11,7 +8,6 @@ function findLayoutFiles(dir, fileList = []) {
11
8
  for (const entry of entries) {
12
9
  const fullPath = path.join(dir, entry.name);
13
10
  if (entry.isDirectory()) {
14
- // Don't traverse node_modules or hidden folders (.next, .git)
15
11
  if (entry.name === 'node_modules' || entry.name.startsWith('.')) {
16
12
  continue;
17
13
  }
@@ -27,30 +23,21 @@ function findLayoutFiles(dir, fileList = []) {
27
23
  }
28
24
  return fileList;
29
25
  }
30
- /**
31
- * Scores a layout file candidate to determine if it is the root layout.
32
- * Higher score = higher likelihood of being the root layout.
33
- */
34
26
  function scoreLayoutCandidate(filePath, projectRoot) {
35
27
  const content = fs.readFileSync(filePath, 'utf-8');
36
28
  const relativePath = path.relative(projectRoot, filePath);
37
29
  const depth = relativePath.split(path.sep).length;
38
- let score = 100 - depth; // Shorter path depth is preferred
39
- // Check for HTML/Body root tags
30
+ let score = 100 - depth;
40
31
  if (/<html/i.test(content))
41
32
  score += 50;
42
33
  if (/<body/i.test(content))
43
34
  score += 50;
44
- // Check if located directly under app/ or src/app/
45
35
  const isDirectAppChild = /^src[/\\]app[/\\]layout\.[jt]sx?$/.test(relativePath) ||
46
36
  /^app[/\\]layout\.[jt]sx?$/.test(relativePath);
47
37
  if (isDirectAppChild)
48
38
  score += 40;
49
39
  return score;
50
40
  }
51
- /**
52
- * Finds the most likely Next.js App Router root layout file in a project.
53
- */
54
41
  export function findRootLayout(projectRoot = process.cwd()) {
55
42
  const possibleAppDirs = [
56
43
  path.join(projectRoot, 'src', 'app'),
@@ -65,7 +52,6 @@ export function findRootLayout(projectRoot = process.cwd()) {
65
52
  if (candidateFiles.length === 0) {
66
53
  return null;
67
54
  }
68
- // Score each candidate to pick the true root layout
69
55
  const scored = candidateFiles.map((filePath) => ({
70
56
  filePath,
71
57
  score: scoreLayoutCandidate(filePath, projectRoot),
@@ -0,0 +1,13 @@
1
+ export type ComponentType = 'badge' | 'watermark' | 'ascii' | 'metadata' | 'all';
2
+ export interface ComponentGeneratorOptions {
3
+ outputDir: string;
4
+ isTypeScript?: boolean;
5
+ }
6
+ export interface GeneratedComponentInfo {
7
+ name: string;
8
+ filePath: string;
9
+ importStatement: string;
10
+ jsxTag: string;
11
+ }
12
+ export declare function generateComponents(types: ComponentType[] | undefined, options: ComponentGeneratorOptions): GeneratedComponentInfo[];
13
+ //# sourceMappingURL=generators.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generators.d.ts","sourceRoot":"","sources":["../src/generators.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,WAAW,GAAG,OAAO,GAAG,UAAU,GAAG,KAAK,CAAC;AAEjF,MAAM,WAAW,yBAAyB;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;CAChB;AA8FD,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,aAAa,EAAE,YAAY,EAClC,OAAO,EAAE,yBAAyB,GACjC,sBAAsB,EAAE,CAkD1B"}
@@ -0,0 +1,134 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ const BADGE_TEMPLATE = `'use client';
4
+ import React from 'react';
5
+
6
+ export function MaysonBadge() {
7
+ return (
8
+ <div
9
+ style={{
10
+ position: 'fixed',
11
+ bottom: '16px',
12
+ right: '16px',
13
+ zIndex: 99999,
14
+ display: 'flex',
15
+ alignItems: 'center',
16
+ gap: '8px',
17
+ padding: '6px 12px',
18
+ backgroundColor: 'rgba(18, 18, 20, 0.9)',
19
+ color: '#ffffff',
20
+ borderRadius: '9999px',
21
+ fontSize: '12px',
22
+ fontWeight: 500,
23
+ fontFamily: 'system-ui, -apple-system, sans-serif',
24
+ border: '1px solid rgba(255, 255, 255, 0.15)',
25
+ boxShadow: '0 4px 14px rgba(0, 0, 0, 0.25)',
26
+ backdropFilter: 'blur(8px)',
27
+ cursor: 'pointer',
28
+ userSelect: 'none',
29
+ transition: 'transform 0.2s ease',
30
+ }}
31
+ >
32
+ <span style={{ width: '8px', height: '8px', borderRadius: '50%', backgroundColor: '#6366f1' }} />
33
+ <span>Built with <strong>Mayson</strong></span>
34
+ </div>
35
+ );
36
+ }
37
+ `;
38
+ const WATERMARK_TEMPLATE = `'use client';
39
+ import React from 'react';
40
+
41
+ export function MaysonWatermark() {
42
+ return (
43
+ <div
44
+ style={{
45
+ position: 'fixed',
46
+ bottom: '12px',
47
+ left: '12px',
48
+ zIndex: 99999,
49
+ opacity: 0.4,
50
+ fontSize: '11px',
51
+ fontFamily: 'monospace',
52
+ color: '#888888',
53
+ pointerEvents: 'none',
54
+ userSelect: 'none',
55
+ }}
56
+ >
57
+ ⚡ Mayson Watermark
58
+ </div>
59
+ );
60
+ }
61
+ `;
62
+ const ASCII_TEMPLATE = `'use client';
63
+ import React, { useEffect } from 'react';
64
+
65
+ const ASCII_ART = \`
66
+ ███╗ ███╗█████╗ ██╗ ██╗███████╗██████╗ ██████╗
67
+ ████╗ ████║██╔══██╗╚██╗ ██╔╝██╔════╝██╔═══██╗██╔══██╗
68
+ ██╔████╔██║███████║ ╚████╔╝ ███████╗██║ ██║██║ ██║
69
+ ██║╚██╔╝██║██╔══██║ ╚██╔╝ ╚════██║██║ ██║██║ ██║
70
+ ██║ ╚═╝ ██║██║ ██║ ██║ ███████║╚██████╔╝██████╔╝
71
+ \`;
72
+
73
+ export function MaysonAscii() {
74
+ useEffect(() => {
75
+ console.log('%c' + ASCII_ART, 'color: #6366f1; font-weight: bold;');
76
+ console.log('%c🚀 Powered by Mayson Platform', 'color: #10b981; font-weight: bold; font-size: 12px;');
77
+ }, []);
78
+
79
+ return null;
80
+ }
81
+ `;
82
+ const METADATA_TEMPLATE = `'use client';
83
+ import React from 'react';
84
+
85
+ export function MaysonMetadata() {
86
+ return (
87
+ <meta name="mayson-generator" content="Mayson App Platform" />
88
+ );
89
+ }
90
+ `;
91
+ export function generateComponents(types = ['badge'], options) {
92
+ const { outputDir, isTypeScript = true } = options;
93
+ const ext = isTypeScript ? '.tsx' : '.jsx';
94
+ const results = [];
95
+ if (!fs.existsSync(outputDir)) {
96
+ fs.mkdirSync(outputDir, { recursive: true });
97
+ }
98
+ const selectedTypes = types.includes('all')
99
+ ? ['badge', 'watermark', 'ascii', 'metadata']
100
+ : types;
101
+ for (const type of selectedTypes) {
102
+ let name = '';
103
+ let code = '';
104
+ switch (type) {
105
+ case 'badge':
106
+ name = 'MaysonBadge';
107
+ code = BADGE_TEMPLATE;
108
+ break;
109
+ case 'watermark':
110
+ name = 'MaysonWatermark';
111
+ code = WATERMARK_TEMPLATE;
112
+ break;
113
+ case 'ascii':
114
+ name = 'MaysonAscii';
115
+ code = ASCII_TEMPLATE;
116
+ break;
117
+ case 'metadata':
118
+ name = 'MaysonMetadata';
119
+ code = METADATA_TEMPLATE;
120
+ break;
121
+ }
122
+ if (name && code) {
123
+ const filePath = path.join(outputDir, `${name}${ext}`);
124
+ fs.writeFileSync(filePath, code, 'utf-8');
125
+ results.push({
126
+ name,
127
+ filePath,
128
+ importStatement: `import { ${name} } from './${name}';`,
129
+ jsxTag: `<${name} />`,
130
+ });
131
+ }
132
+ }
133
+ return results;
134
+ }
package/dist/index.d.ts CHANGED
@@ -1,9 +1,17 @@
1
- import { findRootLayout } from './find-root-layout.js';
2
- import { injectMaysonIframe, InjectorOptions, InjectorResult } from './injector.js';
3
- export { findRootLayout, injectMaysonIframe };
4
- export type { InjectorOptions, InjectorResult };
1
+ import type { ComponentType, InjectorResult } from './core/types.js';
2
+ export { findRootLayout } from './core/find-root-layout.js';
3
+ export { generateComponents } from './core/generators.js';
4
+ export { injectComponentsIntoLayout } from './core/injector.js';
5
+ export { buildRemixRedirectUrl, BADGE_UTM_SOURCE } from './shared/remix-url.js';
6
+ export type { RemixUrlConfig } from './shared/remix-url.js';
7
+ export { loadMaysonEnvConfig } from './shared/config.js';
8
+ export type { MaysonEnvConfig } from './shared/config.js';
9
+ export { fetchGeneratedAppMetadata, resolveMaysonConfig, shouldFetchGeneratedAppMetadata, } from './shared/generated-app-metadata.js';
10
+ export { watermarkPlugin, buildMaysonWatermarkSource, } from './plugins/watermark/index.js';
11
+ export type { ComponentType, GeneratedComponentInfo, ComponentGeneratorOptions, InjectorOptions, InjectorResult, MaysonPlugin, PluginGenerateContext, } from './core/types.js';
5
12
  /**
6
- * Convenience function to automatically locate and inject the Mayson iframe into a Next.js project.
13
+ * Auto-detect root layout, fetch watermark flags from generated-app-metadata when
14
+ * configured, generate component files, and inject them.
7
15
  */
8
- export declare function runAutoInject(projectRoot?: string, iframeUrl?: string, dryRun?: boolean): InjectorResult;
16
+ export declare function runAutoInject(projectRoot?: string, types?: ComponentType[], dryRun?: boolean): Promise<InjectorResult>;
9
17
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpF,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC;AAC9C,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,CAAC;AAEhD;;GAEG;AACH,wBAAgB,aAAa,CAAC,WAAW,GAAE,MAAsB,EAAE,SAAS,GAAE,MAA8B,EAAE,MAAM,GAAE,OAAe,GAAG,cAAc,CAiBrJ"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,aAAa,EACb,cAAc,EAEf,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAChF,YAAY,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,YAAY,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EACL,yBAAyB,EACzB,mBAAmB,EACnB,+BAA+B,GAChC,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EACL,eAAe,EACf,0BAA0B,GAC3B,MAAM,8BAA8B,CAAC;AACtC,YAAY,EACV,aAAa,EACb,sBAAsB,EACtB,yBAAyB,EACzB,eAAe,EACf,cAAc,EACd,YAAY,EACZ,qBAAqB,GACtB,MAAM,iBAAiB,CAAC;AAwBzB;;;GAGG;AACH,wBAAsB,aAAa,CACjC,WAAW,GAAE,MAAsB,EACnC,KAAK,GAAE,aAAa,EAAkB,EACtC,MAAM,GAAE,OAAe,GACtB,OAAO,CAAC,cAAc,CAAC,CA0DzB"}
package/dist/index.js CHANGED
@@ -1,22 +1,78 @@
1
- import { findRootLayout } from './find-root-layout.js';
2
- import { injectMaysonIframe } from './injector.js';
3
- export { findRootLayout, injectMaysonIframe };
1
+ import path from 'node:path';
2
+ import { findRootLayout } from './core/find-root-layout.js';
3
+ import { generateComponents } from './core/generators.js';
4
+ import { injectComponentsIntoLayout } from './core/injector.js';
5
+ import { resolveMaysonConfig } from './shared/generated-app-metadata.js';
6
+ export { findRootLayout } from './core/find-root-layout.js';
7
+ export { generateComponents } from './core/generators.js';
8
+ export { injectComponentsIntoLayout } from './core/injector.js';
9
+ export { buildRemixRedirectUrl, BADGE_UTM_SOURCE } from './shared/remix-url.js';
10
+ export { loadMaysonEnvConfig } from './shared/config.js';
11
+ export { fetchGeneratedAppMetadata, resolveMaysonConfig, shouldFetchGeneratedAppMetadata, } from './shared/generated-app-metadata.js';
12
+ export { watermarkPlugin, buildMaysonWatermarkSource, } from './plugins/watermark/index.js';
13
+ function filterTypesForConfig(types, showRemixPill) {
14
+ const messages = [];
15
+ const selected = types.includes('all')
16
+ ? ['watermark', 'ascii', 'metadata']
17
+ : types;
18
+ if (selected.includes('watermark') && !showRemixPill) {
19
+ messages.push('watermark skipped — show_remix_pill is false (set via generated-app-metadata API)');
20
+ return {
21
+ types: selected.filter((t) => t !== 'watermark'),
22
+ messages,
23
+ };
24
+ }
25
+ return { types: selected, messages };
26
+ }
4
27
  /**
5
- * Convenience function to automatically locate and inject the Mayson iframe into a Next.js project.
28
+ * Auto-detect root layout, fetch watermark flags from generated-app-metadata when
29
+ * configured, generate component files, and inject them.
6
30
  */
7
- export function runAutoInject(projectRoot = process.cwd(), iframeUrl = 'https://mayson.dev/', dryRun = false) {
31
+ export async function runAutoInject(projectRoot = process.cwd(), types = ['watermark'], dryRun = false) {
8
32
  const layoutPath = findRootLayout(projectRoot);
9
33
  if (!layoutPath) {
10
34
  return {
11
35
  success: false,
12
36
  filePath: '',
13
- alreadyInjected: false,
14
- error: `Could not locate Next.js App Router root layout file in ${projectRoot}`,
37
+ injectedComponents: [],
38
+ skippedComponents: [],
39
+ error: `Could not locate Next.js App Router root layout in ${projectRoot}`,
15
40
  };
16
41
  }
17
- return injectMaysonIframe({
42
+ const config = await resolveMaysonConfig();
43
+ const { types: effectiveTypes, messages } = filterTypesForConfig(types, config.showRemixPill);
44
+ if (effectiveTypes.length === 0) {
45
+ return {
46
+ success: true,
47
+ filePath: layoutPath,
48
+ injectedComponents: [],
49
+ skippedComponents: [],
50
+ messages,
51
+ };
52
+ }
53
+ const context = {
54
+ watermark: {
55
+ showRemixPill: config.showRemixPill,
56
+ enableRemix: config.enableRemix,
57
+ appBaseUrl: config.appBaseUrl,
58
+ collectionId: config.collectionId,
59
+ },
60
+ };
61
+ const isTypeScript = layoutPath.endsWith('.tsx') || layoutPath.endsWith('.ts');
62
+ const targetDir = path.dirname(layoutPath);
63
+ const generatedComponents = generateComponents(effectiveTypes, {
64
+ outputDir: targetDir,
65
+ isTypeScript,
66
+ dryRun,
67
+ context,
68
+ });
69
+ const result = injectComponentsIntoLayout({
18
70
  filePath: layoutPath,
19
- iframeUrl,
71
+ components: generatedComponents,
20
72
  dryRun,
21
73
  });
74
+ return {
75
+ ...result,
76
+ messages: [...(result.messages ?? []), ...messages],
77
+ };
22
78
  }
@@ -1,14 +1,16 @@
1
+ import { GeneratedComponentInfo } from './generators.js';
1
2
  export interface InjectorOptions {
2
3
  filePath: string;
3
- iframeUrl?: string;
4
+ components: GeneratedComponentInfo[];
4
5
  dryRun?: boolean;
5
6
  }
6
7
  export interface InjectorResult {
7
8
  success: boolean;
8
9
  filePath: string;
9
- alreadyInjected: boolean;
10
+ injectedComponents: string[];
11
+ skippedComponents: string[];
10
12
  modifiedContent?: string;
11
13
  error?: string;
12
14
  }
13
- export declare function injectMaysonIframe(options: InjectorOptions): InjectorResult;
15
+ export declare function injectComponentsIntoLayout(options: InjectorOptions): InjectorResult;
14
16
  //# sourceMappingURL=injector.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"injector.d.ts","sourceRoot":"","sources":["../src/injector.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,OAAO,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAID,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,eAAe,GAAG,cAAc,CAkF3E"}
1
+ {"version":3,"file":"injector.d.ts","sourceRoot":"","sources":["../src/injector.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,sBAAsB,EAAE,CAAC;IACrC,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,eAAe,GAAG,cAAc,CAwFnF"}
package/dist/injector.js CHANGED
@@ -1,76 +1,82 @@
1
1
  import fs from 'fs';
2
- const INJECTION_MARKER = '/* @mayson-iframe-injected */';
3
- export function injectMaysonIframe(options) {
4
- const { filePath, iframeUrl = 'https://mayson.dev/', dryRun = false } = options;
2
+ import path from 'path';
3
+ export function injectComponentsIntoLayout(options) {
4
+ const { filePath, components, dryRun = false } = options;
5
5
  if (!fs.existsSync(filePath)) {
6
6
  return {
7
7
  success: false,
8
8
  filePath,
9
- alreadyInjected: false,
10
- error: `File not found: ${filePath}`,
9
+ injectedComponents: [],
10
+ skippedComponents: [],
11
+ error: `Layout file not found: ${filePath}`,
11
12
  };
12
13
  }
13
- const content = fs.readFileSync(filePath, 'utf-8');
14
- // Idempotency check: Don't inject twice
15
- if (content.includes(INJECTION_MARKER) || content.includes(iframeUrl)) {
16
- return {
17
- success: true,
18
- filePath,
19
- alreadyInjected: true,
20
- };
21
- }
22
- const snippet = `
23
- {${INJECTION_MARKER}}
24
- <iframe
25
- src="${iframeUrl}"
26
- style={{
27
- position: 'fixed',
28
- bottom: '20px',
29
- right: '20px',
30
- width: '400px',
31
- height: '600px',
32
- border: 'none',
33
- borderRadius: '12px',
34
- boxShadow: '0 8px 32px rgba(0, 0, 0, 0.25)',
35
- zIndex: 999999,
36
- }}
37
- title="Mayson Dev Overlay"
38
- />`;
39
- let updatedContent = null;
40
- // Option 1: Inject before </body>
41
- if (/<\/body>/i.test(content)) {
42
- updatedContent = content.replace(/<\/body>/i, `${snippet}\n </body>`);
43
- }
44
- // Option 2: Inject before </html>
45
- else if (/<\/html>/i.test(content)) {
46
- updatedContent = content.replace(/<\/html>/i, `${snippet}\n </html>`);
47
- }
48
- // Option 3: Inject before the closing tag of the return block (e.g. </main> or </div>)
49
- else {
50
- const lastClosingTagIndex = content.lastIndexOf('</');
51
- if (lastClosingTagIndex !== -1) {
52
- updatedContent =
53
- content.slice(0, lastClosingTagIndex) +
54
- snippet +
55
- '\n ' +
56
- content.slice(lastClosingTagIndex);
14
+ let content = fs.readFileSync(filePath, 'utf-8');
15
+ const injectedComponents = [];
16
+ const skippedComponents = [];
17
+ for (const comp of components) {
18
+ const importMarker = comp.name;
19
+ const jsxMarker = `<${comp.name}`;
20
+ // Skip if already injected
21
+ if (content.includes(importMarker) || content.includes(jsxMarker)) {
22
+ skippedComponents.push(comp.name);
23
+ continue;
57
24
  }
25
+ // Determine component file import path relative to layout.tsx
26
+ const layoutDir = path.dirname(filePath);
27
+ const compDir = path.dirname(comp.filePath);
28
+ const compBaseName = path.basename(comp.filePath, path.extname(comp.filePath));
29
+ let relativeImportPath = path.relative(layoutDir, path.join(compDir, compBaseName));
30
+ if (!relativeImportPath.startsWith('.')) {
31
+ relativeImportPath = `./${relativeImportPath}`;
32
+ }
33
+ // Convert backslashes for Windows path consistency
34
+ relativeImportPath = relativeImportPath.replace(/\\/g, '/');
35
+ const importLine = `import { ${comp.name} } from '${relativeImportPath}';\n`;
36
+ // 1. Insert import statement
37
+ if (/import\s+.*?from\s+['"].*?['"];?/g.test(content)) {
38
+ // Insert after the last import statement
39
+ const importMatches = Array.from(content.matchAll(/import\s+.*?from\s+['"].*?['"];?/g));
40
+ const lastMatch = importMatches[importMatches.length - 1];
41
+ if (lastMatch && lastMatch.index !== undefined) {
42
+ const insertPos = lastMatch.index + lastMatch[0].length;
43
+ content = content.slice(0, insertPos) + '\n' + importLine + content.slice(insertPos);
44
+ }
45
+ else {
46
+ content = importLine + content;
47
+ }
48
+ }
49
+ else {
50
+ content = importLine + content;
51
+ }
52
+ // 2. Insert JSX tag into return block
53
+ const jsxSnippet = `\n {/* @mayson-component-injected: ${comp.name} */}\n ${comp.jsxTag}`;
54
+ if (/<\/body>/i.test(content)) {
55
+ content = content.replace(/<\/body>/i, `${jsxSnippet}\n </body>`);
56
+ }
57
+ else if (/<\/html>/i.test(content)) {
58
+ content = content.replace(/<\/html>/i, `${jsxSnippet}\n </html>`);
59
+ }
60
+ else {
61
+ const lastClosingIndex = content.lastIndexOf('</');
62
+ if (lastClosingIndex !== -1) {
63
+ content =
64
+ content.slice(0, lastClosingIndex) +
65
+ jsxSnippet +
66
+ '\n ' +
67
+ content.slice(lastClosingIndex);
68
+ }
69
+ }
70
+ injectedComponents.push(comp.name);
58
71
  }
59
- if (!updatedContent) {
60
- return {
61
- success: false,
62
- filePath,
63
- alreadyInjected: false,
64
- error: 'Could not find suitable JSX insertion point in layout file.',
65
- };
66
- }
67
- if (!dryRun) {
68
- fs.writeFileSync(filePath, updatedContent, 'utf-8');
72
+ if (injectedComponents.length > 0 && !dryRun) {
73
+ fs.writeFileSync(filePath, content, 'utf-8');
69
74
  }
70
75
  return {
71
76
  success: true,
72
77
  filePath,
73
- alreadyInjected: false,
74
- modifiedContent: updatedContent,
78
+ injectedComponents,
79
+ skippedComponents,
80
+ modifiedContent: content,
75
81
  };
76
82
  }
@@ -0,0 +1,3 @@
1
+ import type { MaysonPlugin } from '../../core/types.js';
2
+ export declare const asciiPlugin: MaysonPlugin;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/plugins/ascii/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAuBxD,eAAO,MAAM,WAAW,EAAE,YAIzB,CAAC"}
@@ -0,0 +1,25 @@
1
+ const ASCII_SOURCE = `'use client';
2
+ import React, { useEffect } from 'react';
3
+
4
+ const ASCII_ART = \`
5
+ ███╗ ███╗█████╗ ██╗ ██╗███████╗██████╗ ██████╗
6
+ ████╗ ████║██╔══██╗╚██╗ ██╔╝██╔════╝██╔═══██╗██╔══██╗
7
+ ██╔████╔██║███████║ ╚████╔╝ ███████╗██║ ██║██║ ██║
8
+ ██║╚██╔╝██║██╔══██║ ╚██╔╝ ╚════██║██║ ██║██║ ██║
9
+ ██║ ╚═╝ ██║██║ ██║ ██║ ███████║╚██████╔╝██████╔╝
10
+ \`;
11
+
12
+ export function MaysonAscii() {
13
+ useEffect(() => {
14
+ console.log('%c' + ASCII_ART, 'color: #6366f1; font-weight: bold;');
15
+ console.log('%c🚀 Powered by Mayson Platform', 'color: #10b981; font-weight: bold; font-size: 12px;');
16
+ }, []);
17
+
18
+ return null;
19
+ }
20
+ `;
21
+ export const asciiPlugin = {
22
+ type: 'ascii',
23
+ name: 'MaysonAscii',
24
+ generateSource: () => ASCII_SOURCE,
25
+ };
@@ -0,0 +1,7 @@
1
+ import type { MaysonPlugin } from '../core/types.js';
2
+ import { asciiPlugin } from './ascii/index.js';
3
+ import { metadataPlugin } from './metadata/index.js';
4
+ import { watermarkPlugin } from './watermark/index.js';
5
+ export declare const plugins: Record<Exclude<MaysonPlugin['type'], never>, MaysonPlugin>;
6
+ export { watermarkPlugin, asciiPlugin, metadataPlugin };
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/plugins/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,EAAE,YAAY,CAI9E,CAAC;AAEF,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,cAAc,EAAE,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { asciiPlugin } from './ascii/index.js';
2
+ import { metadataPlugin } from './metadata/index.js';
3
+ import { watermarkPlugin } from './watermark/index.js';
4
+ export const plugins = {
5
+ watermark: watermarkPlugin,
6
+ ascii: asciiPlugin,
7
+ metadata: metadataPlugin,
8
+ };
9
+ export { watermarkPlugin, asciiPlugin, metadataPlugin };
@@ -0,0 +1,3 @@
1
+ import type { MaysonPlugin } from '../../core/types.js';
2
+ export declare const metadataPlugin: MaysonPlugin;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/plugins/metadata/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAYxD,eAAO,MAAM,cAAc,EAAE,YAI5B,CAAC"}
@@ -0,0 +1,14 @@
1
+ const METADATA_SOURCE = `'use client';
2
+ import React from 'react';
3
+
4
+ export function MaysonMetadata() {
5
+ return (
6
+ <meta name="mayson-generator" content="Mayson App Platform" />
7
+ );
8
+ }
9
+ `;
10
+ export const metadataPlugin = {
11
+ type: 'metadata',
12
+ name: 'MaysonMetadata',
13
+ generateSource: () => METADATA_SOURCE,
14
+ };
@@ -0,0 +1,4 @@
1
+ import type { MaysonPlugin } from '../../core/types.js';
2
+ export declare const watermarkPlugin: MaysonPlugin;
3
+ export { buildMaysonWatermarkSource } from './template.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/plugins/watermark/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGxD,eAAO,MAAM,eAAe,EAAE,YAI7B,CAAC;AAEF,OAAO,EAAE,0BAA0B,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { buildMaysonWatermarkSource } from './template.js';
2
+ export const watermarkPlugin = {
3
+ type: 'watermark',
4
+ name: 'MaysonWatermark',
5
+ generateSource: (context) => buildMaysonWatermarkSource(context),
6
+ };
7
+ export { buildMaysonWatermarkSource } from './template.js';
@@ -0,0 +1,9 @@
1
+ import type { PluginGenerateContext } from '../../core/types.js';
2
+ export type WatermarkTemplateOptions = {
3
+ enableRemix: boolean;
4
+ appBaseUrl: string;
5
+ collectionId: string;
6
+ };
7
+ /** Source for the consumer-app MaysonWatermark client component. */
8
+ export declare function buildMaysonWatermarkSource(context?: PluginGenerateContext): string;
9
+ //# sourceMappingURL=template.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../../../src/plugins/watermark/template.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAIjE,MAAM,MAAM,wBAAwB,GAAG;IACrC,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAaF,oEAAoE;AACpE,wBAAgB,0BAA0B,CACxC,OAAO,CAAC,EAAE,qBAAqB,GAC9B,MAAM,CAqHR"}