@react-trace-enhancer/create-plugin 0.0.3 → 0.0.6
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.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -111,7 +111,7 @@ async function formatOutputFiles(dir) {
|
|
|
111
111
|
//#region src/index.ts
|
|
112
112
|
runMain(defineCommand({
|
|
113
113
|
meta: {
|
|
114
|
-
name: "
|
|
114
|
+
name: "@react-trace-enhancer/create-plugin",
|
|
115
115
|
version: "0.0.1",
|
|
116
116
|
description: "Scaffold a new react-trace plugin"
|
|
117
117
|
},
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/consts.ts","../src/utils.ts","../src/index.ts"],"sourcesContent":["import type { FormatOptions } from 'oxfmt'\n\nexport const TEMPLATE_SOURCE = 'gh:buzinas/react-trace/plugin-template'\n\nexport const USE_LOCAL = process.env['USE_LOCAL'] === 'true'\n\nexport const LOCAL_TEMPLATE_PATH = new URL(\n '../../../plugin-template',\n import.meta.url,\n).pathname\n\nexport const KEBAB_CASE_RE = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/\n\nexport const VALID_SLOTS = ['toolbar', 'actionPanel', 'settings'] as const\nexport type Slot = (typeof VALID_SLOTS)[number]\n\nexport type PackageManager = 'pnpm' | 'npm' | 'yarn' | 'bun'\n\nexport function detectPackageManager(): PackageManager {\n const ua = process.env['npm_config_user_agent'] ?? ''\n if (ua.startsWith('bun')) return 'bun'\n if (ua.startsWith('yarn')) return 'yarn'\n if (ua.startsWith('npm')) return 'npm'\n return 'pnpm'\n}\n\nexport const OXFMT_OPTIONS: FormatOptions = {\n printWidth: 80,\n singleQuote: true,\n semi: false,\n sortImports: {},\n}\n","import { cp, readdir, readFile, rm, writeFile } from 'node:fs/promises'\nimport { basename, join } from 'node:path'\n\nimport * as clack from '@clack/prompts'\nimport { execa } from 'execa'\nimport { Liquid } from 'liquidjs'\nimport { format } from 'oxfmt'\n\nimport { OXFMT_OPTIONS } from './consts'\n\nexport function toPascalCase(str: string): string {\n return str\n .split('-')\n .map((s) => s.charAt(0).toUpperCase() + s.slice(1))\n .join('')\n}\n\nexport function toCamelCase(str: string): string {\n const pascal = toPascalCase(str)\n return pascal.charAt(0).toLowerCase() + pascal.slice(1)\n}\n\nexport function ensureNotCancelled<T>(value: T | symbol): T {\n if (clack.isCancel(value)) {\n clack.cancel('Operation cancelled.')\n process.exit(0)\n }\n return value as T\n}\n\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n retries: number = 2,\n delay: number = 1000,\n): Promise<T> {\n for (let attempt = 0; attempt <= retries; attempt++) {\n try {\n return await fn()\n } catch (error) {\n if (attempt === retries) throw error\n await new Promise((resolve) => setTimeout(resolve, delay))\n }\n }\n throw new Error('Unreachable')\n}\n\nexport async function copyLocalTemplate(\n src: string,\n dest: string,\n): Promise<void> {\n await cp(src, dest, { recursive: true })\n}\n\nexport async function installDependencies(\n pm: string,\n cwd: string,\n onMessage: (msg: string) => void,\n): Promise<void> {\n const subprocess = execa(pm, ['install'], { cwd })\n\n subprocess.stdout?.on('data', (data: Buffer) => {\n const line = data.toString().trim()\n if (line) onMessage(line)\n })\n\n subprocess.stderr?.on('data', (data: Buffer) => {\n const line = data.toString().trim()\n if (line) onMessage(line)\n })\n\n await subprocess\n}\n\nasync function getFiles(dir: string): Promise<string[]> {\n const entries = await readdir(dir, { recursive: true, withFileTypes: true })\n const files: string[] = []\n\n for (const entry of entries) {\n if (entry.isFile()) {\n files.push(join(entry.parentPath, entry.name))\n }\n }\n\n return files\n}\n\nexport async function renderTemplateFiles(\n dir: string,\n data: Record<string, unknown>,\n): Promise<void> {\n const engine = new Liquid()\n engine.registerFilter('pascalCase', (v: string) => toPascalCase(v))\n engine.registerFilter('camelCase', (v: string) => toCamelCase(v))\n\n const files = await getFiles(dir)\n\n for (const filePath of files) {\n if (!filePath.endsWith('.liquid')) continue\n\n const content = await readFile(filePath, 'utf-8')\n const rendered = await engine.parseAndRender(content, data)\n const targetPath = filePath.slice(0, -'.liquid'.length)\n await writeFile(targetPath, rendered)\n await rm(filePath)\n }\n}\n\nexport async function removeEmptyFiles(dir: string): Promise<void> {\n const files = await getFiles(dir)\n\n for (const filePath of files) {\n const content = await readFile(filePath, 'utf-8')\n if (content.trim() === '') {\n await rm(filePath)\n }\n }\n}\n\nexport async function formatOutputFiles(dir: string): Promise<void> {\n const files = await getFiles(dir)\n\n for (const filePath of files) {\n const content = await readFile(filePath, 'utf-8')\n const result = await format(basename(filePath), content, OXFMT_OPTIONS)\n await writeFile(filePath, result.code)\n }\n}\n","import { existsSync, readdirSync } from 'node:fs'\nimport { rm } from 'node:fs/promises'\n\nimport * as clack from '@clack/prompts'\nimport { defineCommand, runMain } from 'citty'\nimport { downloadTemplate } from 'giget'\n\nimport {\n KEBAB_CASE_RE,\n LOCAL_TEMPLATE_PATH,\n TEMPLATE_SOURCE,\n USE_LOCAL,\n VALID_SLOTS,\n detectPackageManager,\n type Slot,\n} from './consts'\nimport {\n copyLocalTemplate,\n ensureNotCancelled,\n formatOutputFiles,\n installDependencies,\n removeEmptyFiles,\n renderTemplateFiles,\n withRetry,\n} from './utils'\n\nconst main = defineCommand({\n meta: {\n name: 'create-react-trace-plugin',\n version: '0.0.1',\n description: 'Scaffold a new react-trace plugin',\n },\n args: {\n name: {\n type: 'string',\n alias: 'n',\n description: 'Plugin name (in kebab-case, e.g. \"my-feature\")',\n },\n description: {\n type: 'string',\n alias: 'd',\n description: 'Plugin description',\n },\n slots: {\n type: 'string',\n alias: 's',\n description:\n 'Comma-separated plugin slots (toolbar,actionPanel,settings)',\n },\n workspace: {\n type: 'boolean',\n alias: 'w',\n description:\n 'Generate for the react-trace monorepo (uses workspace:* deps)',\n default: false,\n },\n install: {\n type: 'boolean',\n alias: 'i',\n description: 'Install dependencies after scaffolding',\n },\n },\n async run({ args }) {\n clack.intro('create-react-trace-plugin')\n\n const invalidName = args.name != null && !KEBAB_CASE_RE.test(args.name)\n const name = ensureNotCancelled(\n args.name != null && !invalidName\n ? args.name\n : await clack.text({\n message: invalidName\n ? 'Plugin name must be kebab-case (e.g. \"my-feature\"):'\n : 'Plugin name (in kebab-case, e.g. \"my-feature\"):',\n placeholder: 'my-feature',\n validate(value) {\n if (!value) return 'Name is required'\n if (!KEBAB_CASE_RE.test(value)) {\n return 'Name must be kebab-case (e.g. \"my-feature\")'\n }\n },\n }),\n )\n\n const emptyDescription =\n args.description != null && args.description.trim() === ''\n const description = ensureNotCancelled(\n args.description != null && !emptyDescription\n ? args.description\n : await clack.text({\n message: emptyDescription\n ? 'Description cannot be empty. Plugin description:'\n : 'Plugin description:',\n placeholder: 'Describe what your plugin does',\n validate(value) {\n if (!value) return 'Description is required'\n },\n }),\n )\n\n const parsedSlots = args.slots\n ?.split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n const invalidSlots =\n parsedSlots != null &&\n !parsedSlots.every((s): s is Slot =>\n (VALID_SLOTS as readonly string[]).includes(s),\n )\n\n const slots = ensureNotCancelled(\n parsedSlots != null && !invalidSlots\n ? (parsedSlots as Slot[])\n : await clack.multiselect({\n message: invalidSlots\n ? 'Invalid slot names. Pick from the list below:'\n : 'Which plugin slots do you want to scaffold?',\n options: [\n {\n value: 'toolbar' as const,\n label: 'Toolbar',\n hint: 'Button in the widget toolbar',\n },\n {\n value: 'actionPanel' as const,\n label: 'Action panel',\n hint: 'Item in the component action dropdown',\n },\n {\n value: 'settings' as const,\n label: 'Settings',\n hint: 'Section in the widget settings (includes store)',\n },\n ],\n required: false,\n }),\n )\n\n const workspace = args.workspace\n const packageName = workspace\n ? `@react-trace-enhancer/plugin-${name}`\n : `react-trace-plugin-${name}`\n const outputDir = workspace ? `packages/plugin-${name}` : `plugin-${name}`\n\n const templateData = {\n pluginName: name,\n packageName,\n pluginDescription: description,\n workspace,\n hasToolbar: slots.includes('toolbar'),\n hasActionPanel: slots.includes('actionPanel'),\n hasSettings: slots.includes('settings'),\n }\n\n const dirExists = existsSync(outputDir) && readdirSync(outputDir).length > 0\n\n if (dirExists) {\n const resolution = ensureNotCancelled(\n await clack.select({\n message: `Directory ${outputDir} already exists. What do you want to do?`,\n options: [\n {\n value: 'overwrite' as const,\n label: 'Overwrite',\n hint: 'Merge into existing directory, overwrite colliding files',\n },\n {\n value: 'clean' as const,\n label: 'Clear and recreate',\n hint: 'Delete the directory and start fresh',\n },\n { value: 'abort' as const, label: 'Abort' },\n ],\n }),\n )\n\n if (resolution === 'abort') {\n clack.cancel('Operation cancelled.')\n process.exit(0)\n }\n\n if (resolution === 'clean') {\n await rm(outputDir, { recursive: true, force: true })\n }\n }\n\n const s = clack.spinner()\n\n if (USE_LOCAL) {\n s.start('Copying local template...')\n try {\n await copyLocalTemplate(LOCAL_TEMPLATE_PATH, outputDir)\n } catch (error) {\n s.stop('Failed to copy template.')\n clack.cancel(\n error instanceof Error ? error.message : 'Template copy failed',\n )\n process.exit(1)\n }\n s.stop('Template copied.')\n } else {\n s.start('Downloading template...')\n try {\n await withRetry(() =>\n downloadTemplate(TEMPLATE_SOURCE, {\n dir: outputDir,\n force: dirExists,\n }),\n )\n } catch (error) {\n s.stop('Failed to download template.')\n clack.cancel(\n error instanceof Error ? error.message : 'Template download failed',\n )\n process.exit(1)\n }\n s.stop('Template downloaded.')\n }\n\n s.start('Rendering template...')\n await renderTemplateFiles(outputDir, templateData)\n await removeEmptyFiles(outputDir)\n await formatOutputFiles(outputDir)\n s.stop('Plugin scaffolded.')\n\n const pm = detectPackageManager()\n\n const shouldInstall = ensureNotCancelled(\n args.install ??\n (await clack.confirm({\n message: 'Install dependencies?',\n })),\n )\n\n if (shouldInstall) {\n s.start(`Installing dependencies with ${pm}...`)\n try {\n await installDependencies(pm, outputDir, (msg) => s.message(msg))\n s.stop('Dependencies installed.')\n } catch (error) {\n s.stop('Failed to install dependencies.')\n clack.log.warn(\n error instanceof Error ? error.message : 'Install failed',\n )\n }\n }\n\n const nextSteps = [`cd ${outputDir}`]\n if (!shouldInstall) nextSteps.push(`${pm} install`)\n nextSteps.push(`${pm} build`)\n\n clack.note(nextSteps.join('\\n'), 'Next steps')\n\n clack.outro(`Plugin ${packageName} created.`)\n },\n})\n\nrunMain(main)\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/consts.ts","../src/utils.ts","../src/index.ts"],"sourcesContent":["import type { FormatOptions } from 'oxfmt'\n\nexport const TEMPLATE_SOURCE = 'gh:buzinas/react-trace/plugin-template'\n\nexport const USE_LOCAL = process.env['USE_LOCAL'] === 'true'\n\nexport const LOCAL_TEMPLATE_PATH = new URL(\n '../../../plugin-template',\n import.meta.url,\n).pathname\n\nexport const KEBAB_CASE_RE = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/\n\nexport const VALID_SLOTS = ['toolbar', 'actionPanel', 'settings'] as const\nexport type Slot = (typeof VALID_SLOTS)[number]\n\nexport type PackageManager = 'pnpm' | 'npm' | 'yarn' | 'bun'\n\nexport function detectPackageManager(): PackageManager {\n const ua = process.env['npm_config_user_agent'] ?? ''\n if (ua.startsWith('bun')) return 'bun'\n if (ua.startsWith('yarn')) return 'yarn'\n if (ua.startsWith('npm')) return 'npm'\n return 'pnpm'\n}\n\nexport const OXFMT_OPTIONS: FormatOptions = {\n printWidth: 80,\n singleQuote: true,\n semi: false,\n sortImports: {},\n}\n","import { cp, readdir, readFile, rm, writeFile } from 'node:fs/promises'\nimport { basename, join } from 'node:path'\n\nimport * as clack from '@clack/prompts'\nimport { execa } from 'execa'\nimport { Liquid } from 'liquidjs'\nimport { format } from 'oxfmt'\n\nimport { OXFMT_OPTIONS } from './consts'\n\nexport function toPascalCase(str: string): string {\n return str\n .split('-')\n .map((s) => s.charAt(0).toUpperCase() + s.slice(1))\n .join('')\n}\n\nexport function toCamelCase(str: string): string {\n const pascal = toPascalCase(str)\n return pascal.charAt(0).toLowerCase() + pascal.slice(1)\n}\n\nexport function ensureNotCancelled<T>(value: T | symbol): T {\n if (clack.isCancel(value)) {\n clack.cancel('Operation cancelled.')\n process.exit(0)\n }\n return value as T\n}\n\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n retries: number = 2,\n delay: number = 1000,\n): Promise<T> {\n for (let attempt = 0; attempt <= retries; attempt++) {\n try {\n return await fn()\n } catch (error) {\n if (attempt === retries) throw error\n await new Promise((resolve) => setTimeout(resolve, delay))\n }\n }\n throw new Error('Unreachable')\n}\n\nexport async function copyLocalTemplate(\n src: string,\n dest: string,\n): Promise<void> {\n await cp(src, dest, { recursive: true })\n}\n\nexport async function installDependencies(\n pm: string,\n cwd: string,\n onMessage: (msg: string) => void,\n): Promise<void> {\n const subprocess = execa(pm, ['install'], { cwd })\n\n subprocess.stdout?.on('data', (data: Buffer) => {\n const line = data.toString().trim()\n if (line) onMessage(line)\n })\n\n subprocess.stderr?.on('data', (data: Buffer) => {\n const line = data.toString().trim()\n if (line) onMessage(line)\n })\n\n await subprocess\n}\n\nasync function getFiles(dir: string): Promise<string[]> {\n const entries = await readdir(dir, { recursive: true, withFileTypes: true })\n const files: string[] = []\n\n for (const entry of entries) {\n if (entry.isFile()) {\n files.push(join(entry.parentPath, entry.name))\n }\n }\n\n return files\n}\n\nexport async function renderTemplateFiles(\n dir: string,\n data: Record<string, unknown>,\n): Promise<void> {\n const engine = new Liquid()\n engine.registerFilter('pascalCase', (v: string) => toPascalCase(v))\n engine.registerFilter('camelCase', (v: string) => toCamelCase(v))\n\n const files = await getFiles(dir)\n\n for (const filePath of files) {\n if (!filePath.endsWith('.liquid')) continue\n\n const content = await readFile(filePath, 'utf-8')\n const rendered = await engine.parseAndRender(content, data)\n const targetPath = filePath.slice(0, -'.liquid'.length)\n await writeFile(targetPath, rendered)\n await rm(filePath)\n }\n}\n\nexport async function removeEmptyFiles(dir: string): Promise<void> {\n const files = await getFiles(dir)\n\n for (const filePath of files) {\n const content = await readFile(filePath, 'utf-8')\n if (content.trim() === '') {\n await rm(filePath)\n }\n }\n}\n\nexport async function formatOutputFiles(dir: string): Promise<void> {\n const files = await getFiles(dir)\n\n for (const filePath of files) {\n const content = await readFile(filePath, 'utf-8')\n const result = await format(basename(filePath), content, OXFMT_OPTIONS)\n await writeFile(filePath, result.code)\n }\n}\n","import { existsSync, readdirSync } from 'node:fs'\nimport { rm } from 'node:fs/promises'\n\nimport * as clack from '@clack/prompts'\nimport { defineCommand, runMain } from 'citty'\nimport { downloadTemplate } from 'giget'\n\nimport {\n KEBAB_CASE_RE,\n LOCAL_TEMPLATE_PATH,\n TEMPLATE_SOURCE,\n USE_LOCAL,\n VALID_SLOTS,\n detectPackageManager,\n type Slot,\n} from './consts'\nimport {\n copyLocalTemplate,\n ensureNotCancelled,\n formatOutputFiles,\n installDependencies,\n removeEmptyFiles,\n renderTemplateFiles,\n withRetry,\n} from './utils'\n\nconst main = defineCommand({\n meta: {\n name: '@react-trace-enhancer/create-plugin',\n version: '0.0.1',\n description: 'Scaffold a new react-trace plugin',\n },\n args: {\n name: {\n type: 'string',\n alias: 'n',\n description: 'Plugin name (in kebab-case, e.g. \"my-feature\")',\n },\n description: {\n type: 'string',\n alias: 'd',\n description: 'Plugin description',\n },\n slots: {\n type: 'string',\n alias: 's',\n description:\n 'Comma-separated plugin slots (toolbar,actionPanel,settings)',\n },\n workspace: {\n type: 'boolean',\n alias: 'w',\n description:\n 'Generate for the react-trace monorepo (uses workspace:* deps)',\n default: false,\n },\n install: {\n type: 'boolean',\n alias: 'i',\n description: 'Install dependencies after scaffolding',\n },\n },\n async run({ args }) {\n clack.intro('create-react-trace-plugin')\n\n const invalidName = args.name != null && !KEBAB_CASE_RE.test(args.name)\n const name = ensureNotCancelled(\n args.name != null && !invalidName\n ? args.name\n : await clack.text({\n message: invalidName\n ? 'Plugin name must be kebab-case (e.g. \"my-feature\"):'\n : 'Plugin name (in kebab-case, e.g. \"my-feature\"):',\n placeholder: 'my-feature',\n validate(value) {\n if (!value) return 'Name is required'\n if (!KEBAB_CASE_RE.test(value)) {\n return 'Name must be kebab-case (e.g. \"my-feature\")'\n }\n },\n }),\n )\n\n const emptyDescription =\n args.description != null && args.description.trim() === ''\n const description = ensureNotCancelled(\n args.description != null && !emptyDescription\n ? args.description\n : await clack.text({\n message: emptyDescription\n ? 'Description cannot be empty. Plugin description:'\n : 'Plugin description:',\n placeholder: 'Describe what your plugin does',\n validate(value) {\n if (!value) return 'Description is required'\n },\n }),\n )\n\n const parsedSlots = args.slots\n ?.split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n const invalidSlots =\n parsedSlots != null &&\n !parsedSlots.every((s): s is Slot =>\n (VALID_SLOTS as readonly string[]).includes(s),\n )\n\n const slots = ensureNotCancelled(\n parsedSlots != null && !invalidSlots\n ? (parsedSlots as Slot[])\n : await clack.multiselect({\n message: invalidSlots\n ? 'Invalid slot names. Pick from the list below:'\n : 'Which plugin slots do you want to scaffold?',\n options: [\n {\n value: 'toolbar' as const,\n label: 'Toolbar',\n hint: 'Button in the widget toolbar',\n },\n {\n value: 'actionPanel' as const,\n label: 'Action panel',\n hint: 'Item in the component action dropdown',\n },\n {\n value: 'settings' as const,\n label: 'Settings',\n hint: 'Section in the widget settings (includes store)',\n },\n ],\n required: false,\n }),\n )\n\n const workspace = args.workspace\n const packageName = workspace\n ? `@react-trace-enhancer/plugin-${name}`\n : `react-trace-plugin-${name}`\n const outputDir = workspace ? `packages/plugin-${name}` : `plugin-${name}`\n\n const templateData = {\n pluginName: name,\n packageName,\n pluginDescription: description,\n workspace,\n hasToolbar: slots.includes('toolbar'),\n hasActionPanel: slots.includes('actionPanel'),\n hasSettings: slots.includes('settings'),\n }\n\n const dirExists = existsSync(outputDir) && readdirSync(outputDir).length > 0\n\n if (dirExists) {\n const resolution = ensureNotCancelled(\n await clack.select({\n message: `Directory ${outputDir} already exists. What do you want to do?`,\n options: [\n {\n value: 'overwrite' as const,\n label: 'Overwrite',\n hint: 'Merge into existing directory, overwrite colliding files',\n },\n {\n value: 'clean' as const,\n label: 'Clear and recreate',\n hint: 'Delete the directory and start fresh',\n },\n { value: 'abort' as const, label: 'Abort' },\n ],\n }),\n )\n\n if (resolution === 'abort') {\n clack.cancel('Operation cancelled.')\n process.exit(0)\n }\n\n if (resolution === 'clean') {\n await rm(outputDir, { recursive: true, force: true })\n }\n }\n\n const s = clack.spinner()\n\n if (USE_LOCAL) {\n s.start('Copying local template...')\n try {\n await copyLocalTemplate(LOCAL_TEMPLATE_PATH, outputDir)\n } catch (error) {\n s.stop('Failed to copy template.')\n clack.cancel(\n error instanceof Error ? error.message : 'Template copy failed',\n )\n process.exit(1)\n }\n s.stop('Template copied.')\n } else {\n s.start('Downloading template...')\n try {\n await withRetry(() =>\n downloadTemplate(TEMPLATE_SOURCE, {\n dir: outputDir,\n force: dirExists,\n }),\n )\n } catch (error) {\n s.stop('Failed to download template.')\n clack.cancel(\n error instanceof Error ? error.message : 'Template download failed',\n )\n process.exit(1)\n }\n s.stop('Template downloaded.')\n }\n\n s.start('Rendering template...')\n await renderTemplateFiles(outputDir, templateData)\n await removeEmptyFiles(outputDir)\n await formatOutputFiles(outputDir)\n s.stop('Plugin scaffolded.')\n\n const pm = detectPackageManager()\n\n const shouldInstall = ensureNotCancelled(\n args.install ??\n (await clack.confirm({\n message: 'Install dependencies?',\n })),\n )\n\n if (shouldInstall) {\n s.start(`Installing dependencies with ${pm}...`)\n try {\n await installDependencies(pm, outputDir, (msg) => s.message(msg))\n s.stop('Dependencies installed.')\n } catch (error) {\n s.stop('Failed to install dependencies.')\n clack.log.warn(\n error instanceof Error ? error.message : 'Install failed',\n )\n }\n }\n\n const nextSteps = [`cd ${outputDir}`]\n if (!shouldInstall) nextSteps.push(`${pm} install`)\n nextSteps.push(`${pm} build`)\n\n clack.note(nextSteps.join('\\n'), 'Next steps')\n\n clack.outro(`Plugin ${packageName} created.`)\n },\n})\n\nrunMain(main)\n"],"mappings":"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"}
|