@unlockable/vite-plugin-unlock 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 +21 -0
- package/README.md +346 -0
- package/dist/index.cjs +809 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +29 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +772 -0
- package/dist/index.js.map +1 -0
- package/dist/medusa.cjs +136 -0
- package/dist/medusa.cjs.map +1 -0
- package/dist/medusa.d.cts +75 -0
- package/dist/medusa.d.ts +75 -0
- package/dist/medusa.js +109 -0
- package/dist/medusa.js.map +1 -0
- package/dist/types-C3wWFKIK.d.cts +105 -0
- package/dist/types-C3wWFKIK.d.ts +105 -0
- package/package.json +77 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/plugin.ts","../src/config.ts","../src/constants.ts","../src/utils.ts","../src/scanner.ts","../src/resolver.ts","../src/watcher.ts"],"sourcesContent":["import path from \"path\"\nimport fs from \"fs\"\nimport type { Plugin } from \"vite\"\nimport type { UnlockOptions, ResolvedTarget } from \"./types\"\nimport type { ResolverState } from \"./resolver\"\nimport { resolveOptions } from \"./config\"\nimport { scanAllTargets, scanTarget, scanOverrides } from \"./scanner\"\nimport {\n resolveEntryRedirect,\n findImporterTarget,\n} from \"./resolver\"\nimport { setupWatcher } from \"./watcher\"\nimport { createLogger, normalizePath, stripExtension } from \"./utils\"\nimport { PLUGIN_NAME } from \"./constants\"\n\n/**\n * Create the vite-plugin-unlock plugin.\n *\n * Unlocks modules from target npm packages by filename convention.\n * Place a file with the same name in your overrides directory and it\n * will replace the original module — with full HMR support.\n *\n * @example\n * ```ts\n * // vite.config.ts\n * import { unlock } from '@unlockable/vite-plugin-unlock'\n *\n * export default defineConfig({\n * plugins: [\n * unlock({\n * targets: ['@acme/dashboard'],\n * overrides: './src/overrides',\n * })\n * ]\n * })\n * ```\n */\nexport function unlock(userOptions: UnlockOptions): Plugin {\n const opts = resolveOptions(userOptions)\n const logger = createLogger(opts.debug)\n\n if (opts.targets.length === 0) {\n logger.warn(\"No target packages found. Plugin will be inactive.\")\n return { name: PLUGIN_NAME }\n }\n\n // Scan target packages for all component/hook files\n const targetFiles = scanAllTargets(opts, logger)\n\n // Scan override directories\n const { flat, namespaced } = scanOverrides(opts, logger)\n\n // Filter flat overrides to only those matching target files\n const fileOverrides = new Map<string, string>()\n for (const [key, overridePath] of flat) {\n if (targetFiles.has(key)) {\n fileOverrides.set(key, overridePath)\n } else {\n logger.warn(\n `Override \"${key}\" does not match any file in target packages — skipped`\n )\n }\n }\n\n // Detect conflicts in multi-target mode\n if (opts.targets.length > 1 && fileOverrides.size > 0) {\n detectConflicts(fileOverrides, targetFiles, opts, logger, scanTarget)\n }\n\n const state: ResolverState = {\n flatOverrides: fileOverrides,\n namespacedOverrides: namespaced,\n targetFiles,\n }\n\n // Log active overrides\n const totalFileOverrides =\n fileOverrides.size +\n [...namespaced.values()].reduce((sum, m) => sum + m.size, 0)\n\n if (totalFileOverrides > 0) {\n logger.info(`Active overrides: ${totalFileOverrides}`)\n if (fileOverrides.size > 0) {\n logger.info(` File-level: ${[...fileOverrides.keys()].join(\", \")}`)\n }\n for (const [ns, map] of namespaced) {\n logger.info(` [${ns}]: ${[...map.keys()].join(\", \")}`)\n }\n }\n\n // Log active patches\n for (const patch of opts.patches) {\n if (patch.configPath) {\n logger.info(`Patch: ${patch.configFile} -> ${patch.configPath}`)\n } else {\n logger.info(`Patch: ${patch.configFile} (no config file found)`)\n }\n }\n\n return {\n name: PLUGIN_NAME,\n enforce: \"pre\",\n\n config(config) {\n // Allow override directories in Vite's FS access (needed for HMR)\n config.server = config.server || {}\n config.server.fs = config.server.fs || {}\n config.server.fs.allow = config.server.fs.allow || []\n for (const dir of opts.overrideDirs) {\n config.server.fs.allow.push(path.resolve(dir))\n }\n\n // Exclude targets from pre-bundling\n config.optimizeDeps = config.optimizeDeps || {}\n config.optimizeDeps.exclude = config.optimizeDeps.exclude || []\n\n for (const target of opts.targets) {\n config.optimizeDeps.exclude!.push(target.package)\n\n // Remove from include (include beats exclude in Vite)\n if (config.optimizeDeps.include) {\n config.optimizeDeps.include = config.optimizeDeps.include.filter(\n (dep) => dep !== target.package\n )\n }\n }\n\n // Set up aliases for each target\n config.resolve = config.resolve || {}\n config.resolve.alias = config.resolve.alias || {}\n\n for (const target of opts.targets) {\n if (Array.isArray(config.resolve.alias)) {\n config.resolve.alias.push({\n find: target.alias,\n replacement: target.srcPath,\n })\n } else {\n ;(config.resolve.alias as Record<string, string>)[target.alias] =\n target.srcPath\n }\n }\n\n // Add target source entries for Vite to discover CJS deps\n for (const target of opts.targets) {\n const entryFile = path.join(target.srcPath, \"app.tsx\")\n if (fs.existsSync(entryFile)) {\n const existing = config.optimizeDeps.entries\n if (Array.isArray(existing)) {\n existing.push(entryFile)\n } else if (typeof existing === \"string\") {\n config.optimizeDeps.entries = [existing, entryFile]\n } else {\n config.optimizeDeps.entries = [entryFile]\n }\n }\n }\n },\n\n async resolveId(source, importer) {\n if (source.startsWith(\"\\0\") || !importer || importer.startsWith(\"\\0\"))\n return null\n\n const target = findImporterTarget(importer, opts)\n\n if (target) {\n // Importer is inside a target package\n\n // 1. File-level override\n const basename = stripExtension(path.basename(source))\n if (basename && basename !== \"index\") {\n const nsOverrides = state.namespacedOverrides.get(target.package)\n if (nsOverrides?.has(basename)) {\n const p = nsOverrides.get(basename)!\n logger.info(\n `Override [${target.package}]: ${basename} -> ${path.basename(p)}`\n )\n return p\n }\n if (state.flatOverrides.has(basename)) {\n const p = state.flatOverrides.get(basename)!\n logger.info(`Override: ${basename} -> ${path.basename(p)}`)\n return p\n }\n }\n\n // 2. Entry redirect — only for imports of the target package itself\n if (\n source === target.package ||\n source.startsWith(target.package + \"/\")\n ) {\n const resolved = await this.resolve(source, importer, {\n skipSelf: true,\n })\n if (resolved) {\n const redirect = resolveEntryRedirect(\n resolved.id,\n opts,\n logger\n )\n if (redirect) return redirect\n }\n }\n\n return null\n }\n\n // Importer is NOT in a target package — entry redirect only\n for (const target of opts.targets) {\n if (\n source !== target.package &&\n !source.startsWith(target.package + \"/\")\n )\n continue\n\n const resolved = await this.resolve(source, importer, {\n skipSelf: true,\n })\n if (resolved) {\n const redirect = resolveEntryRedirect(resolved.id, opts, logger)\n if (redirect) return redirect\n }\n }\n\n return null\n },\n\n load(id) {\n const target = findImporterTarget(id, opts)\n if (!target) return null\n\n const basename = stripExtension(path.basename(id))\n if (!basename || basename === \"index\") return null\n\n // 1. File-level override\n const nsOverrides = state.namespacedOverrides.get(target.package)\n const overridePath =\n nsOverrides?.get(basename) ?? state.flatOverrides.get(basename)\n if (overridePath && fs.existsSync(overridePath)) {\n this.addWatchFile(overridePath)\n\n const normalizedPath = normalizePath(overridePath).replace(/\"/g, '\\\\\"')\n logger.info(\n `Load override: ${basename} -> ${path.basename(overridePath)}`\n )\n return `export { default } from \"${normalizedPath}\"\\nexport * from \"${normalizedPath}\"`\n }\n\n // 2. Patches: apply code transformation driven by a config file\n const normalizedId = normalizePath(id)\n for (const patch of opts.patches) {\n if (!patch.target.test(normalizedId)) continue\n if (!patch.configPath || !fs.existsSync(patch.configPath)) continue\n\n this.addWatchFile(patch.configPath)\n let original: string\n try {\n original = fs.readFileSync(id, \"utf-8\")\n } catch (err) {\n logger.error(`Failed to read file for patching: ${id}`)\n return null\n }\n const patched = patch.apply(original, patch.configPath)\n logger.info(\n `Patch applied: ${path.basename(id)} via ${patch.configFile}`\n )\n return { code: patched, map: null }\n }\n\n return null\n },\n\n transform(code, id) {\n // Target-level HMR: CSS redirect + entry boundary\n for (const target of opts.targets) {\n if (!target.hmr || !target.entryFilePath) continue\n\n const normId = normalizePath(id)\n const normEntry = normalizePath(target.entryFilePath)\n if (normId !== normEntry) continue\n\n let modified = false\n\n if (target.hmr.cssRedirect) {\n const { from, to } = target.hmr.cssRedirect\n const importPattern = `import \"${from}\"`\n if (code.includes(importPattern)) {\n code = code.replace(importPattern, `import \"${to}\"`)\n logger.info(`CSS rewritten: ${from} -> ${to}`)\n modified = true\n }\n }\n\n if (target.hmr.entryBoundary) {\n code += \"\\nif (import.meta.hot) { import.meta.hot.accept() }\"\n logger.info(\n `HMR boundary injected: ${target.package} entry`\n )\n modified = true\n }\n\n if (modified) return { code, map: null }\n }\n\n // Content-based HMR boundaries\n if (opts.hmrBoundaries.length > 0) {\n const normId = normalizePath(id)\n if (!normId.includes(\"/node_modules/\")) {\n const needsBoundary = opts.hmrBoundaries.some((p) =>\n code.includes(p)\n )\n if (needsBoundary) {\n logger.info(\n `HMR boundary injected: ${path.basename(id)}`\n )\n return {\n code:\n code +\n \"\\nif (import.meta.hot) { import.meta.hot.accept() }\",\n map: null,\n }\n }\n }\n }\n\n return null\n },\n\n configureServer(server) {\n const fsConfig = server.config.server?.fs\n if (fsConfig && Array.isArray(fsConfig.allow)) {\n for (const dir of opts.overrideDirs) {\n const resolved = path.resolve(dir)\n if (!fsConfig.allow.includes(resolved)) {\n fsConfig.allow.push(resolved)\n }\n }\n }\n\n setupWatcher(server, state, opts, logger)\n },\n }\n}\n\n/**\n * Detect conflicting overrides across multiple targets.\n * For each flat override key, check how many targets actually contain a file with that basename.\n */\nfunction detectConflicts(\n flat: Map<string, string>,\n _targetFiles: Map<string, { target: ResolvedTarget; filePath: string }>,\n opts: ReturnType<typeof resolveOptions>,\n logger: ReturnType<typeof createLogger>,\n scan: typeof scanTarget\n): void {\n // Build per-target file maps to check which targets contain each key\n const perTargetMaps = opts.targets.map((t) => ({\n target: t,\n files: scan(t, opts),\n }))\n\n for (const [key] of flat) {\n const matchingTargets = perTargetMaps.filter(({ files }) => files.has(key))\n\n if (matchingTargets.length > 1) {\n const names = matchingTargets.map(({ target }) => target.package).join(\", \")\n if (opts.onConflict === \"error\") {\n throw new Error(\n `[${PLUGIN_NAME}] Override \"${key}\" matches files in multiple targets: ${names}. ` +\n `Use namespaced overrides (overrides/@scope/pkg/${key}.tsx) or set onConflict: \"warn\".`\n )\n } else if (opts.onConflict === \"warn\") {\n logger.warn(\n `Override \"${key}\" matches files in multiple targets: ${names}. Using first match.`\n )\n }\n }\n }\n}\n","import path from \"path\"\nimport fs from \"fs\"\nimport { createRequire } from \"module\"\nimport type {\n UnlockOptions,\n UnlockTargetInput,\n ResolvedOptions,\n ResolvedPatch,\n ResolvedTarget,\n} from \"./types\"\nimport {\n DEFAULT_EXTENSIONS,\n DEFAULT_OVERRIDE_DIR,\n DEFAULT_SRC_DIR,\n} from \"./constants\"\nimport { generateAlias } from \"./utils\"\n\n/**\n * Find the source directory of an npm package.\n * Looks in node_modules and .yalc for the package, then checks for srcDir within it.\n */\nfunction findPackageSrcPath(\n packageName: string,\n srcDir: string\n): string | null {\n const cwd = process.cwd()\n\n // Try require.resolve to find the package\n try {\n const req = createRequire(path.join(cwd, \"package.json\"))\n const pkgJsonPath = req.resolve(`${packageName}/package.json`)\n const pkgRoot = path.dirname(pkgJsonPath)\n const srcPath = path.join(pkgRoot, srcDir)\n if (fs.existsSync(srcPath)) return srcPath\n return pkgRoot\n } catch {\n // Fallback: manual lookup\n }\n\n // Manual lookup in common locations\n const parts = packageName.startsWith(\"@\")\n ? packageName.split(\"/\")\n : [packageName]\n const candidates = [\n path.join(cwd, \"node_modules\", ...parts, srcDir),\n path.join(cwd, \".yalc\", ...parts, srcDir),\n ]\n\n for (const dir of candidates) {\n if (fs.existsSync(dir)) return dir\n }\n\n const rootCandidates = [\n path.join(cwd, \"node_modules\", ...parts),\n path.join(cwd, \".yalc\", ...parts),\n ]\n\n for (const dir of rootCandidates) {\n if (fs.existsSync(dir)) return dir\n }\n\n return null\n}\n\n/**\n * Normalize a single target input into a ResolvedTarget.\n */\nfunction resolveTarget(input: UnlockTargetInput): ResolvedTarget | null {\n const target: ResolvedTarget =\n typeof input === \"string\"\n ? {\n package: input,\n alias: generateAlias(input),\n srcDir: DEFAULT_SRC_DIR,\n srcPath: \"\",\n }\n : {\n package: input.package,\n alias: input.alias ?? generateAlias(input.package),\n srcDir: input.srcDir ?? DEFAULT_SRC_DIR,\n srcPath: \"\",\n entryRedirect: input.entryRedirect,\n hmr: input.hmr,\n }\n\n const srcPath = findPackageSrcPath(target.package, target.srcDir)\n if (!srcPath) return null\n\n target.srcPath = srcPath\n\n // Pre-compute entry file path for HMR transforms\n if (target.entryRedirect && target.hmr) {\n const pkgDir = path.dirname(srcPath)\n const entryFile = path.resolve(pkgDir, target.entryRedirect.to)\n if (fs.existsSync(entryFile)) {\n target.entryFilePath = entryFile\n }\n }\n\n return target\n}\n\nconst CONFIG_EXTENSIONS = [\".tsx\", \".ts\", \".jsx\", \".js\"]\n\n/**\n * Find a config file by basename in the override directories.\n * Searches for `basename.{tsx,ts,jsx,js}` in each dir.\n */\nexport function findConfigFile(\n basename: string,\n overrideDirs: string[]\n): string | null {\n for (const dir of overrideDirs) {\n for (const ext of CONFIG_EXTENSIONS) {\n const p = path.resolve(dir, `${basename}${ext}`)\n if (fs.existsSync(p)) return p\n }\n }\n return null\n}\n\n/**\n * Resolve and normalize all plugin options.\n */\nexport function resolveOptions(options: UnlockOptions): ResolvedOptions {\n const extensions = options.extensions ?? DEFAULT_EXTENSIONS\n\n const overrideInput = options.overrides ?? DEFAULT_OVERRIDE_DIR\n const overrideDirs = (\n Array.isArray(overrideInput) ? overrideInput : [overrideInput]\n ).map((dir) => path.resolve(process.cwd(), dir))\n\n const targets: ResolvedTarget[] = []\n for (const input of options.targets) {\n const resolved = resolveTarget(input)\n if (resolved) {\n targets.push(resolved)\n }\n }\n\n const patches: ResolvedPatch[] = (options.patches ?? []).map((p) => ({\n target: p.target,\n configFile: p.configFile,\n apply: p.apply,\n configPath: findConfigFile(p.configFile, overrideDirs),\n }))\n\n return {\n targets,\n overrideDirs,\n match: options.match ?? \"basename\",\n onConflict: options.onConflict ?? \"error\",\n debug: options.debug ?? false,\n extensions,\n extensionSet: new Set(extensions),\n patches,\n hmrBoundaries: options.hmrBoundaries ?? [],\n }\n}\n","/** Default file extensions to scan for overridable modules. */\nexport const DEFAULT_EXTENSIONS = [\n \".tsx\",\n \".ts\",\n \".jsx\",\n \".js\",\n \".vue\",\n \".svelte\",\n \".mts\",\n \".mjs\",\n]\n\n/** Default directory for override files. */\nexport const DEFAULT_OVERRIDE_DIR = \"./src/overrides\"\n\n/** Default subdirectory within a package that contains source files. */\nexport const DEFAULT_SRC_DIR = \"src\"\n\n/** Maximum recursion depth when scanning directories. */\nexport const MAX_SCAN_DEPTH = 20\n\n/** Plugin name used in Vite. */\nexport const PLUGIN_NAME = \"vite-plugin-unlock\"\n","import { PLUGIN_NAME } from \"./constants\"\n\nconst PREFIX = `[${PLUGIN_NAME}]`\n\n/**\n * Logger that only outputs when debug is enabled.\n */\nexport function createLogger(debug: boolean) {\n return {\n info(msg: string) {\n if (debug) console.log(`${PREFIX} ${msg}`)\n },\n warn(msg: string) {\n console.warn(`${PREFIX} ${msg}`)\n },\n error(msg: string) {\n console.error(`${PREFIX} ${msg}`)\n },\n }\n}\n\nexport type Logger = ReturnType<typeof createLogger>\n\n/**\n * Normalize a file path to forward slashes.\n */\nexport function normalizePath(p: string): string {\n return p.replace(/\\\\/g, \"/\")\n}\n\n/**\n * Strip extension from a filename.\n */\nexport function stripExtension(filename: string): string {\n return filename.replace(/\\.(tsx?|jsx?|mts|mjs|vue|svelte)$/, \"\")\n}\n\n/**\n * Generate an alias from a package name.\n * \"@acme/dashboard\" -> \"~dashboard\"\n * \"@acme/ui\" -> \"~ui\"\n * \"my-lib\" -> \"~my-lib\"\n */\nexport function generateAlias(packageName: string): string {\n const parts = packageName.split(\"/\")\n const lastPart = parts[parts.length - 1]\n return `~${lastPart}`\n}\n","import path from \"path\"\nimport fs from \"fs\"\nimport type {\n OverrideMap,\n NamespacedOverrideMap,\n ResolvedOptions,\n ResolvedTarget,\n} from \"./types\"\nimport { MAX_SCAN_DEPTH } from \"./constants\"\nimport { normalizePath, stripExtension, type Logger } from \"./utils\"\n\n/**\n * Recursively collect files with matching extensions from a directory.\n */\nexport function collectFiles(\n dir: string,\n extensionSet: Set<string>,\n depth = 0\n): string[] {\n if (depth > MAX_SCAN_DEPTH) return []\n const results: string[] = []\n let entries: fs.Dirent[]\n try {\n entries = fs.readdirSync(dir, { withFileTypes: true })\n } catch {\n return results\n }\n for (const entry of entries) {\n if (entry.name.startsWith(\".\") || entry.name === \"node_modules\") continue\n const fullPath = path.resolve(dir, entry.name)\n if (entry.isDirectory()) {\n results.push(...collectFiles(fullPath, extensionSet, depth + 1))\n } else if (entry.isFile()) {\n const ext = path.extname(entry.name)\n if (extensionSet.has(ext)) {\n results.push(fullPath)\n }\n }\n }\n return results\n}\n\n/**\n * Extract the override key from a file path based on match strategy.\n *\n * For \"basename\" strategy: returns the filename without extension.\n * For \"path\" strategy: returns the relative path from the override dir without extension.\n */\nexport function getOverrideKey(\n filePath: string,\n baseDir: string,\n match: \"basename\" | \"path\"\n): string | null {\n const normalized = normalizePath(filePath)\n const parts = normalized.split(\"/\").filter(Boolean)\n if (parts.length === 0) return null\n\n const fileName = parts[parts.length - 1]\n const baseName = stripExtension(fileName)\n if (!baseName) return null\n\n if (match === \"path\") {\n const normalizedBase = normalizePath(baseDir)\n const relative = normalizePath(\n path.relative(normalizedBase, normalized)\n )\n return stripExtension(relative)\n }\n\n // basename strategy\n if (baseName === \"index\") {\n return parts.length >= 2 ? parts[parts.length - 2] || null : null\n }\n return baseName\n}\n\n/**\n * Scan a target package and build a map of component name -> file path.\n */\nexport function scanTarget(\n target: ResolvedTarget,\n opts: ResolvedOptions\n): OverrideMap {\n const map: OverrideMap = new Map()\n for (const f of collectFiles(target.srcPath, opts.extensionSet)) {\n const key = getOverrideKey(f, target.srcPath, opts.match)\n if (key) map.set(key, f)\n }\n return map\n}\n\n/**\n * Scan all target packages and build a combined map of all component files.\n */\nexport function scanAllTargets(\n opts: ResolvedOptions,\n logger: Logger\n): Map<string, { target: ResolvedTarget; filePath: string }> {\n const combined = new Map<\n string,\n { target: ResolvedTarget; filePath: string }\n >()\n\n for (const target of opts.targets) {\n const targetMap = scanTarget(target, opts)\n logger.info(\n `Scanned ${targetMap.size} files in ${target.package} (${target.srcPath})`\n )\n for (const [key, filePath] of targetMap) {\n combined.set(key, { target, filePath })\n }\n }\n\n return combined\n}\n\n/**\n * Detect if a directory path represents a namespaced override\n * (e.g. overrides/@acme/ui/ -> package \"@acme/ui\").\n */\nfunction detectNamespace(\n filePath: string,\n overrideDir: string\n): string | null {\n const relative = normalizePath(path.relative(overrideDir, filePath))\n const scopedMatch = relative.match(/^(@[^/]+\\/[^/]+)\\//)\n if (scopedMatch) return scopedMatch[1]\n return null\n}\n\n/**\n * Scan override directories and build override maps.\n *\n * Returns both a flat override map (for single-target or basename matching)\n * and a namespaced map (for multi-target with namespace directories).\n */\nexport function scanOverrides(\n opts: ResolvedOptions,\n logger: Logger\n): { flat: OverrideMap; namespaced: NamespacedOverrideMap } {\n const flat: OverrideMap = new Map()\n const namespaced: NamespacedOverrideMap = new Map()\n\n const targetPackages = new Set(opts.targets.map((t) => t.package))\n\n for (const dir of opts.overrideDirs) {\n if (!fs.existsSync(dir)) continue\n for (const fullPath of collectFiles(dir, opts.extensionSet).sort()) {\n // Skip files in directories starting with _\n const relative = normalizePath(path.relative(dir, fullPath))\n if (relative.split(\"/\").some((part) => part.startsWith(\"_\"))) continue\n\n const ns = detectNamespace(fullPath, dir)\n\n if (ns && targetPackages.has(ns)) {\n const nsDir = path.join(dir, ns)\n const key = getOverrideKey(fullPath, nsDir, opts.match)\n if (key && key !== \"index\") {\n if (!namespaced.has(ns)) namespaced.set(ns, new Map())\n namespaced.get(ns)!.set(key, fullPath)\n logger.info(`Override [${ns}]: ${key} -> ${fullPath}`)\n }\n } else {\n const key = getOverrideKey(fullPath, dir, opts.match)\n if (key && key !== \"index\") {\n flat.set(key, fullPath)\n logger.info(`Override: ${key} -> ${fullPath}`)\n }\n }\n }\n }\n\n return { flat, namespaced }\n}\n","import path from \"path\"\nimport type {\n OverrideMap,\n NamespacedOverrideMap,\n ResolvedOptions,\n ResolvedTarget,\n} from \"./types\"\nimport { normalizePath, type Logger } from \"./utils\"\nimport fs from \"fs\"\n\nexport interface ResolverState {\n /** component/hook name -> override file path (flat, applies to all targets) */\n flatOverrides: OverrideMap\n /** package name -> (component name -> override file path) */\n namespacedOverrides: NamespacedOverrideMap\n /** component name -> { target, filePath } for all target packages */\n targetFiles: Map<string, { target: ResolvedTarget; filePath: string }>\n}\n\n/**\n * Check if an importer belongs to a target package.\n */\nexport function findImporterTarget(\n importer: string,\n opts: ResolvedOptions\n): ResolvedTarget | null {\n const norm = normalizePath(importer)\n for (const target of opts.targets) {\n const normSrc = normalizePath(target.srcPath)\n if (norm.startsWith(normSrc + \"/\") || norm === normSrc) {\n return target\n }\n // Also check parent package dir (for files outside srcDir)\n const pkgDir = normalizePath(path.dirname(target.srcPath))\n if (norm.startsWith(pkgDir + \"/\")) {\n return target\n }\n }\n return null\n}\n\n/**\n * Handle entry redirect: remap dist entry to source entry.\n * e.g. dist/app.mjs -> src/app.tsx\n */\nexport function resolveEntryRedirect(\n resolvedId: string,\n opts: ResolvedOptions,\n logger: Logger\n): string | null {\n const norm = normalizePath(resolvedId).replace(/\\?.*$/, \"\")\n\n for (const target of opts.targets) {\n if (target.entryRedirect) {\n const fromPattern = normalizePath(target.entryRedirect.from)\n if (norm.endsWith(`/${fromPattern}`) || norm.includes(`/${fromPattern}`)) {\n const pkgDir = path.dirname(target.srcPath)\n const srcEntry = path.join(pkgDir, target.entryRedirect.to)\n if (fs.existsSync(srcEntry)) {\n logger.info(\n `Entry redirect: ${path.basename(resolvedId)} -> ${target.entryRedirect.to}`\n )\n return srcEntry\n }\n }\n }\n\n // Default entry redirect: dist/app.{mjs,js} -> src/app.tsx\n if (!target.entryRedirect) {\n const parts = target.package.split(\"/\")\n const lastPart = parts[parts.length - 1]\n if (norm.includes(`/${lastPart}/dist/app.`)) {\n const srcEntry = norm.replace(\n /\\/dist\\/app\\.(mjs|js)$/,\n \"/src/app.tsx\"\n )\n if (fs.existsSync(srcEntry)) {\n logger.info(`Entry redirect (auto): dist/app -> src/app.tsx`)\n return srcEntry\n }\n }\n }\n }\n\n return null\n}\n","import path from \"path\"\nimport type { ViteDevServer } from \"vite\"\nimport type { ResolvedOptions, ResolvedPatch } from \"./types\"\nimport { scanOverrides } from \"./scanner\"\nimport { findConfigFile } from \"./config\"\nimport type { ResolverState } from \"./resolver\"\nimport { normalizePath, stripExtension, type Logger } from \"./utils\"\n\n/**\n * Set up HMR watchers for override directories.\n *\n * Only reacts to structural changes (file add/delete).\n * Content edits to override files are handled by Vite's native HMR\n * (React Fast Refresh) — the plugin does NOT intercept them.\n */\nexport function setupWatcher(\n server: ViteDevServer,\n state: ResolverState,\n opts: ResolvedOptions,\n logger: Logger\n): void {\n let debounceTimer: ReturnType<typeof setTimeout> | null = null\n\n const handleStructuralChange = (filePath: string) => {\n const ext = path.extname(filePath)\n if (!opts.extensionSet.has(ext)) return\n\n const normFile = normalizePath(filePath)\n const isOverrideFile = opts.overrideDirs.some((dir) =>\n normFile.startsWith(normalizePath(dir) + \"/\")\n )\n if (!isOverrideFile) return\n\n if (debounceTimer) clearTimeout(debounceTimer)\n debounceTimer = setTimeout(() => {\n const oldFlat = new Map(state.flatOverrides)\n const oldNamespaced = new Map(\n [...state.namespacedOverrides].map(\n ([k, v]) => [k, new Map(v)] as const\n )\n )\n\n const { flat, namespaced } = scanOverrides(opts, logger)\n\n // Filter flat overrides to only those matching target files\n const fileOverrides = new Map<string, string>()\n for (const [key, overridePath] of flat) {\n if (state.targetFiles.has(key)) {\n fileOverrides.set(key, overridePath)\n }\n }\n\n // Atomic swap: update both maps in a single synchronous block\n // so that async resolveId calls always see a consistent pair.\n Object.assign(state, {\n flatOverrides: fileOverrides,\n namespacedOverrides: namespaced,\n })\n\n let hasChanges = false\n\n // Diff file-level overrides\n const allKeys = new Set([...oldFlat.keys(), ...fileOverrides.keys()])\n\n for (const key of allKeys) {\n const wasOverride = oldFlat.has(key)\n const isOverride = fileOverrides.has(key)\n if (\n wasOverride === isOverride &&\n oldFlat.get(key) === fileOverrides.get(key)\n )\n continue\n\n const action = isOverride\n ? wasOverride\n ? \"changed\"\n : \"created\"\n : \"deleted\"\n logger.info(`Override \"${key}\" ${action}`)\n\n invalidateForKey(key, isOverride, oldFlat, state, server, logger)\n hasChanges = true\n }\n\n // Diff namespaced overrides\n const allNs = new Set([\n ...oldNamespaced.keys(),\n ...namespaced.keys(),\n ])\n for (const ns of allNs) {\n const oldMap = oldNamespaced.get(ns) ?? new Map()\n const newMap = namespaced.get(ns) ?? new Map()\n const nsKeys = new Set([...oldMap.keys(), ...newMap.keys()])\n\n for (const key of nsKeys) {\n const was = oldMap.has(key)\n const is = newMap.has(key)\n if (was === is && oldMap.get(key) === newMap.get(key)) continue\n\n const action = is ? (was ? \"changed\" : \"created\") : \"deleted\"\n logger.info(`Override [${ns}] \"${key}\" ${action}`)\n\n invalidateForKey(key, is, oldMap, state, server, logger)\n hasChanges = true\n }\n }\n\n // Single full-reload for all structural changes.\n // Structural changes alter module resolution — Vite's HMR cannot\n // handle this because invalidated modules reuse cached resolved ids.\n if (hasChanges) {\n logger.info(\"Structural change detected -> full reload\")\n const ws = server.hot ?? server.ws\n ws.send({ type: \"full-reload\" })\n }\n }, 50) // Short debounce — batch rapid add/delete\n }\n\n // Structural changes (add/delete)\n server.watcher.on(\"add\", handleStructuralChange)\n server.watcher.on(\"unlink\", handleStructuralChange)\n\n // Content edits to override files.\n // When Vite tracks the override file (resolveId path), its native HMR\n // handles content edits via React Fast Refresh. But when the load hook\n // served override content at the original URL, Vite doesn't associate\n // the override file with any module — edits go undetected.\n server.watcher.on(\"change\", (filePath) => {\n const ext = path.extname(filePath)\n if (!opts.extensionSet.has(ext)) return\n\n const normFile = normalizePath(filePath)\n const isOverrideFile = opts.overrideDirs.some((dir) =>\n normFile.startsWith(normalizePath(dir) + \"/\")\n )\n if (!isOverrideFile) return\n\n const basename = stripExtension(path.basename(filePath))\n if (!basename) return\n const isTrackedOverride =\n state.flatOverrides.has(basename) ||\n [...state.namespacedOverrides.values()].some((m) => m.has(basename))\n if (!isTrackedOverride) return\n\n // If Vite already tracks this file, let native HMR handle it\n const trackedMods = server.moduleGraph.getModulesByFile(normFile)\n if (trackedMods && trackedMods.size > 0) return\n\n // Load hook case: invalidate the original module and reload\n const targetInfo = state.targetFiles.get(basename)\n if (!targetInfo) return\n\n const origMods = server.moduleGraph.getModulesByFile(\n normalizePath(targetInfo.filePath)\n )\n if (origMods) {\n for (const mod of origMods) {\n server.moduleGraph.invalidateModule(mod)\n }\n logger.info(`Override content changed: ${basename} -> reload`)\n const ws = (server as any).hot ?? (server as any).ws\n ws.send({ type: \"full-reload\" })\n }\n })\n\n // Watch for patch config file creation / deletion / content edit\n if (opts.patches.length > 0) {\n const handlePatchConfigStructural = (filePath: string) => {\n const basename = stripExtension(path.basename(filePath))\n if (!basename) return\n\n const normFile = normalizePath(filePath)\n const isOverrideFile = opts.overrideDirs.some((dir) =>\n normFile.startsWith(normalizePath(dir) + \"/\")\n )\n if (!isOverrideFile) return\n\n for (const patch of opts.patches) {\n if (basename !== patch.configFile) continue\n\n const newPath = findConfigFile(patch.configFile, opts.overrideDirs)\n if (newPath === patch.configPath) continue\n\n patch.configPath = newPath\n logger.info(\n `Patch config ${newPath ? \"detected\" : \"removed\"}: ${patch.configFile}`\n )\n\n invalidatePatchTarget(patch, server, logger)\n const ws = (server as any).hot ?? (server as any).ws\n ws.send({ type: \"full-reload\" })\n }\n }\n\n const handlePatchConfigContentEdit = (filePath: string) => {\n const normFile = normalizePath(filePath)\n for (const patch of opts.patches) {\n if (!patch.configPath) continue\n if (normalizePath(patch.configPath) !== normFile) continue\n\n logger.info(`Patch config content changed: ${patch.configFile}`)\n invalidatePatchTarget(patch, server, logger)\n const ws = (server as any).hot ?? (server as any).ws\n ws.send({ type: \"full-reload\" })\n return\n }\n }\n\n server.watcher.on(\"add\", handlePatchConfigStructural)\n server.watcher.on(\"unlink\", handlePatchConfigStructural)\n server.watcher.on(\"change\", handlePatchConfigContentEdit)\n }\n\n // Ensure override directories are watched\n for (const dir of opts.overrideDirs) {\n server.watcher.add(dir)\n }\n}\n\n/**\n * Invalidate modules matching a patch target pattern,\n * plus all ancestor modules (BFS) so that import URLs\n * are updated and the browser re-fetches fresh content.\n */\nfunction invalidatePatchTarget(\n patch: ResolvedPatch,\n server: ViteDevServer,\n logger: Logger\n): void {\n const { moduleGraph } = server\n const roots = new Set<any>()\n const timestamp = Date.now()\n\n for (const mod of moduleGraph.idToModuleMap.values()) {\n if (mod.file && patch.target.test(normalizePath(mod.file))) {\n moduleGraph.invalidateModule(mod, new Set(), timestamp, true)\n roots.add(mod)\n logger.info(`Invalidated patch target: ${path.basename(mod.file)}`)\n }\n }\n\n // BFS invalidate all ancestors so import URLs are cache-busted\n const seen = new Set<any>()\n const queue: any[] = []\n for (const mod of roots) {\n for (const parent of mod.importers) {\n queue.push(parent)\n }\n }\n while (queue.length > 0) {\n const mod = queue.shift()!\n if (seen.has(mod)) continue\n seen.add(mod)\n moduleGraph.invalidateModule(mod, seen, timestamp, true)\n for (const parent of mod.importers) {\n queue.push(parent)\n }\n }\n\n if (roots.size > 0 && seen.size > 0) {\n logger.info(`Invalidated ${seen.size} ancestor modules`)\n }\n}\n\n/**\n * Invalidate modules in the graph for a given override key change.\n */\nfunction invalidateForKey(\n key: string,\n isNowOverride: boolean,\n oldOverrides: Map<string, string>,\n state: ResolverState,\n server: ViteDevServer,\n logger: Logger\n): void {\n const { moduleGraph } = server\n\n const roots = new Set<any>()\n\n // Original target file's module\n const targetInfo = state.targetFiles.get(key)\n if (targetInfo) {\n const mods = moduleGraph.getModulesByFile(\n normalizePath(targetInfo.filePath)\n )\n if (mods) for (const mod of mods) roots.add(mod)\n }\n\n // Override file's module (old for DELETE, new for CREATE)\n const overridePath = isNowOverride\n ? state.flatOverrides.get(key)\n : oldOverrides.get(key)\n if (overridePath) {\n const mods = moduleGraph.getModulesByFile(normalizePath(overridePath))\n if (mods) for (const mod of mods) roots.add(mod)\n }\n\n if (roots.size > 0) {\n for (const mod of roots) {\n moduleGraph.invalidateModule(mod)\n }\n\n // BFS invalidate all ancestors\n const seen = new Set<any>()\n const queue: any[] = []\n for (const mod of roots) {\n for (const parent of mod.importers) {\n queue.push(parent)\n }\n }\n while (queue.length > 0) {\n const mod = queue.shift()!\n if (seen.has(mod)) continue\n seen.add(mod)\n moduleGraph.invalidateModule(mod)\n for (const parent of mod.importers) {\n queue.push(parent)\n }\n }\n\n logger.info(`Invalidated \"${key}\" + ${seen.size} ancestor modules`)\n } else {\n logger.info(`Override map updated for \"${key}\" (module not in graph yet)`)\n }\n}\n"],"mappings":"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","names":["path","fs","path","fs","fs","path","path","fs","path","path","path","path","fs","target"]}
|
package/dist/medusa.cjs
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/medusa.ts
|
|
21
|
+
var medusa_exports = {};
|
|
22
|
+
__export(medusa_exports, {
|
|
23
|
+
medusa: () => medusa
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(medusa_exports);
|
|
26
|
+
|
|
27
|
+
// src/presets/menu-patch.ts
|
|
28
|
+
function patchMenuLayout(code, configPath) {
|
|
29
|
+
const coreMarker = "const coreRoutes = useCoreRoutes()";
|
|
30
|
+
const extMarker = 'const menuItems = getMenu("coreExtensions").filter((item) => !item.nested)';
|
|
31
|
+
if (!code.includes(coreMarker)) {
|
|
32
|
+
return code;
|
|
33
|
+
}
|
|
34
|
+
const safePath = configPath.replace(/\\/g, "/").replace(/"/g, '\\"');
|
|
35
|
+
const importLine = `import __menuConfig from "${safePath}";
|
|
36
|
+
import * as __React from "react";
|
|
37
|
+
import __i18n from "i18next";
|
|
38
|
+
`;
|
|
39
|
+
let patched = code.replace(
|
|
40
|
+
coreMarker,
|
|
41
|
+
"const coreRoutes = __applyMenuConfig(useCoreRoutes(), __menuConfig)"
|
|
42
|
+
);
|
|
43
|
+
if (patched.includes(extMarker)) {
|
|
44
|
+
patched = patched.replace(
|
|
45
|
+
extMarker,
|
|
46
|
+
'const menuItems = getMenu("coreExtensions").filter((item) => !item.nested).filter(function(item) { return !__getPromotedPaths(__menuConfig).includes(item.to) })'
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
const helperFn = `
|
|
50
|
+
function __toNavItems(list) {
|
|
51
|
+
return list.map(function(item) {
|
|
52
|
+
var icon = typeof item.icon === "function" || (item.icon && item.icon.render) ? __React.createElement(item.icon) : item.icon;
|
|
53
|
+
var label = __i18n.t(item.label);
|
|
54
|
+
var kids = item.items;
|
|
55
|
+
var translatedKids = kids && kids.length > 0 ? kids.map(function(k) { return { label: __i18n.t(k.label), to: k.to }; }) : undefined;
|
|
56
|
+
return { icon: icon, label: label, to: item.to, items: translatedKids };
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function __applyMenuConfig(routes, config) {
|
|
61
|
+
if (!config) return routes;
|
|
62
|
+
if (typeof config === "function") return __toNavItems(config(routes));
|
|
63
|
+
if (config.items) return __toNavItems(config.items);
|
|
64
|
+
let result = [...routes];
|
|
65
|
+
if (config.remove) {
|
|
66
|
+
result = result.filter(function(r) { return !config.remove.includes(r.to) });
|
|
67
|
+
}
|
|
68
|
+
if (config.add) {
|
|
69
|
+
result = result.concat(__toNavItems(config.add));
|
|
70
|
+
}
|
|
71
|
+
if (config.order) {
|
|
72
|
+
var ordered = [];
|
|
73
|
+
var rest = [].concat(result);
|
|
74
|
+
for (var k = 0; k < config.order.length; k++) {
|
|
75
|
+
var p = config.order[k];
|
|
76
|
+
var i = rest.findIndex(function(r) { return r.to === p });
|
|
77
|
+
if (i !== -1) ordered.push(rest.splice(i, 1)[0]);
|
|
78
|
+
}
|
|
79
|
+
result = ordered.concat(rest);
|
|
80
|
+
}
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function __getPromotedPaths(config) {
|
|
85
|
+
if (!config || typeof config === "function") return [];
|
|
86
|
+
var paths = [];
|
|
87
|
+
if (config.items) {
|
|
88
|
+
config.items.forEach(function(item) { paths.push(item.to) });
|
|
89
|
+
}
|
|
90
|
+
if (config.add) {
|
|
91
|
+
config.add.forEach(function(item) { paths.push(item.to) });
|
|
92
|
+
}
|
|
93
|
+
return paths;
|
|
94
|
+
}
|
|
95
|
+
`;
|
|
96
|
+
return importLine + patched + helperFn;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// src/presets/medusa.ts
|
|
100
|
+
function medusa(options) {
|
|
101
|
+
const {
|
|
102
|
+
overrides = "./src/admin/overrides",
|
|
103
|
+
debug = false
|
|
104
|
+
} = options ?? {};
|
|
105
|
+
return {
|
|
106
|
+
targets: [
|
|
107
|
+
{
|
|
108
|
+
package: "@medusajs/dashboard",
|
|
109
|
+
alias: "~dashboard",
|
|
110
|
+
entryRedirect: { from: "dist/app.mjs", to: "src/app.tsx" },
|
|
111
|
+
hmr: {
|
|
112
|
+
cssRedirect: { from: "./index.css", to: "../dist/app.css" },
|
|
113
|
+
entryBoundary: true
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
],
|
|
117
|
+
overrides,
|
|
118
|
+
debug,
|
|
119
|
+
hmrBoundaries: [
|
|
120
|
+
"defineRouteConfig",
|
|
121
|
+
"defineWidgetConfig"
|
|
122
|
+
],
|
|
123
|
+
patches: [
|
|
124
|
+
{
|
|
125
|
+
target: /main-layout\.tsx$/,
|
|
126
|
+
configFile: "menu.config",
|
|
127
|
+
apply: patchMenuLayout
|
|
128
|
+
}
|
|
129
|
+
]
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
133
|
+
0 && (module.exports = {
|
|
134
|
+
medusa
|
|
135
|
+
});
|
|
136
|
+
//# sourceMappingURL=medusa.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/medusa.ts","../src/presets/menu-patch.ts","../src/presets/medusa.ts"],"sourcesContent":["export { medusa, type MedusaPresetOptions } from \"./presets/medusa\"\nexport type { NavItemConfig, MenuConfig, MenuConfigFn } from \"./presets/types\"\n","/**\n * Pure patch function for the Medusa dashboard sidebar menu.\n *\n * Transforms main-layout.tsx to inject a menu config import and\n * wrap useCoreRoutes() with __applyMenuConfig().\n *\n * Used as a `patches[].apply` function by vite-plugin-unlock.\n */\nexport function patchMenuLayout(code: string, configPath: string): string {\n const coreMarker = \"const coreRoutes = useCoreRoutes()\"\n const extMarker =\n 'const menuItems = getMenu(\"coreExtensions\").filter((item) => !item.nested)'\n\n if (!code.includes(coreMarker)) {\n return code\n }\n\n const safePath = configPath.replace(/\\\\/g, \"/\").replace(/\"/g, '\\\\\"')\n const importLine = `import __menuConfig from \"${safePath}\";\\nimport * as __React from \"react\";\\nimport __i18n from \"i18next\";\\n`\n\n let patched = code.replace(\n coreMarker,\n \"const coreRoutes = __applyMenuConfig(useCoreRoutes(), __menuConfig)\"\n )\n\n if (patched.includes(extMarker)) {\n patched = patched.replace(\n extMarker,\n 'const menuItems = getMenu(\"coreExtensions\").filter((item) => !item.nested).filter(function(item) { return !__getPromotedPaths(__menuConfig).includes(item.to) })'\n )\n }\n\n const helperFn = `\nfunction __toNavItems(list) {\n return list.map(function(item) {\n var icon = typeof item.icon === \"function\" || (item.icon && item.icon.render) ? __React.createElement(item.icon) : item.icon;\n var label = __i18n.t(item.label);\n var kids = item.items;\n var translatedKids = kids && kids.length > 0 ? kids.map(function(k) { return { label: __i18n.t(k.label), to: k.to }; }) : undefined;\n return { icon: icon, label: label, to: item.to, items: translatedKids };\n });\n}\n\nfunction __applyMenuConfig(routes, config) {\n if (!config) return routes;\n if (typeof config === \"function\") return __toNavItems(config(routes));\n if (config.items) return __toNavItems(config.items);\n let result = [...routes];\n if (config.remove) {\n result = result.filter(function(r) { return !config.remove.includes(r.to) });\n }\n if (config.add) {\n result = result.concat(__toNavItems(config.add));\n }\n if (config.order) {\n var ordered = [];\n var rest = [].concat(result);\n for (var k = 0; k < config.order.length; k++) {\n var p = config.order[k];\n var i = rest.findIndex(function(r) { return r.to === p });\n if (i !== -1) ordered.push(rest.splice(i, 1)[0]);\n }\n result = ordered.concat(rest);\n }\n return result;\n}\n\nfunction __getPromotedPaths(config) {\n if (!config || typeof config === \"function\") return [];\n var paths = [];\n if (config.items) {\n config.items.forEach(function(item) { paths.push(item.to) });\n }\n if (config.add) {\n config.add.forEach(function(item) { paths.push(item.to) });\n }\n return paths;\n}\n`\n\n return importLine + patched + helperFn\n}\n","import type { UnlockOptions } from \"../types\"\nimport { patchMenuLayout } from \"./menu-patch\"\n\nexport interface MedusaPresetOptions {\n /**\n * Directory (or directories) containing override files.\n * @default \"./src/admin/overrides\"\n */\n overrides?: string | string[]\n\n /**\n * Enable debug logging.\n * @default false\n */\n debug?: boolean\n}\n\n/**\n * Medusa dashboard preset for vite-plugin-unlock.\n *\n * Pre-configured to target `@medusajs/dashboard` with sensible defaults:\n * - Entry redirect (unbundles dashboard source)\n * - CSS redirect (avoids Tailwind reprocessing)\n * - HMR boundaries for admin extensions\n * - Menu patching via `menu.config.ts`\n *\n * @example\n * ```ts\n * import { unlock } from \"@unlockable/vite-plugin-unlock\"\n * import { medusa } from \"@unlockable/vite-plugin-unlock/medusa\"\n *\n * export default defineConfig({\n * plugins: [unlock(medusa())],\n * })\n * ```\n */\nexport function medusa(options?: MedusaPresetOptions): UnlockOptions {\n const {\n overrides = \"./src/admin/overrides\",\n debug = false,\n } = options ?? {}\n\n return {\n targets: [\n {\n package: \"@medusajs/dashboard\",\n alias: \"~dashboard\",\n entryRedirect: { from: \"dist/app.mjs\", to: \"src/app.tsx\" },\n hmr: {\n cssRedirect: { from: \"./index.css\", to: \"../dist/app.css\" },\n entryBoundary: true,\n },\n },\n ],\n overrides,\n debug,\n hmrBoundaries: [\n \"defineRouteConfig\",\n \"defineWidgetConfig\",\n ],\n patches: [\n {\n target: /main-layout\\.tsx$/,\n configFile: \"menu.config\",\n apply: patchMenuLayout,\n },\n ],\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACQO,SAAS,gBAAgB,MAAc,YAA4B;AACxE,QAAM,aAAa;AACnB,QAAM,YACJ;AAEF,MAAI,CAAC,KAAK,SAAS,UAAU,GAAG;AAC9B,WAAO;AAAA,EACT;AAEA,QAAM,WAAW,WAAW,QAAQ,OAAO,GAAG,EAAE,QAAQ,MAAM,KAAK;AACnE,QAAM,aAAa,6BAA6B,QAAQ;AAAA;AAAA;AAAA;AAExD,MAAI,UAAU,KAAK;AAAA,IACjB;AAAA,IACA;AAAA,EACF;AAEA,MAAI,QAAQ,SAAS,SAAS,GAAG;AAC/B,cAAU,QAAQ;AAAA,MAChB;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,QAAM,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgDjB,SAAO,aAAa,UAAU;AAChC;;;AC7CO,SAAS,OAAO,SAA8C;AACnE,QAAM;AAAA,IACJ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,IAAI,WAAW,CAAC;AAEhB,SAAO;AAAA,IACL,SAAS;AAAA,MACP;AAAA,QACE,SAAS;AAAA,QACT,OAAO;AAAA,QACP,eAAe,EAAE,MAAM,gBAAgB,IAAI,cAAc;AAAA,QACzD,KAAK;AAAA,UACH,aAAa,EAAE,MAAM,eAAe,IAAI,kBAAkB;AAAA,UAC1D,eAAe;AAAA,QACjB;AAAA,MACF;AAAA,IACF;AAAA,IACA;AAAA,IACA;AAAA,IACA,eAAe;AAAA,MACb;AAAA,MACA;AAAA,IACF;AAAA,IACA,SAAS;AAAA,MACP;AAAA,QACE,QAAQ;AAAA,QACR,YAAY;AAAA,QACZ,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { U as UnlockOptions } from './types-C3wWFKIK.cjs';
|
|
2
|
+
|
|
3
|
+
interface MedusaPresetOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Directory (or directories) containing override files.
|
|
6
|
+
* @default "./src/admin/overrides"
|
|
7
|
+
*/
|
|
8
|
+
overrides?: string | string[];
|
|
9
|
+
/**
|
|
10
|
+
* Enable debug logging.
|
|
11
|
+
* @default false
|
|
12
|
+
*/
|
|
13
|
+
debug?: boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Medusa dashboard preset for vite-plugin-unlock.
|
|
17
|
+
*
|
|
18
|
+
* Pre-configured to target `@medusajs/dashboard` with sensible defaults:
|
|
19
|
+
* - Entry redirect (unbundles dashboard source)
|
|
20
|
+
* - CSS redirect (avoids Tailwind reprocessing)
|
|
21
|
+
* - HMR boundaries for admin extensions
|
|
22
|
+
* - Menu patching via `menu.config.ts`
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* import { unlock } from "@unlockable/vite-plugin-unlock"
|
|
27
|
+
* import { medusa } from "@unlockable/vite-plugin-unlock/medusa"
|
|
28
|
+
*
|
|
29
|
+
* export default defineConfig({
|
|
30
|
+
* plugins: [unlock(medusa())],
|
|
31
|
+
* })
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
declare function medusa(options?: MedusaPresetOptions): UnlockOptions;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* A menu item configuration.
|
|
38
|
+
* Matches the shape of @medusajs/dashboard's internal nav items.
|
|
39
|
+
*/
|
|
40
|
+
interface NavItemConfig {
|
|
41
|
+
/** Icon — pass a component directly (e.g. ShoppingCart from @medusajs/icons) or a React element */
|
|
42
|
+
icon?: unknown;
|
|
43
|
+
/** Display label */
|
|
44
|
+
label: string;
|
|
45
|
+
/** Route path (e.g. "/orders") */
|
|
46
|
+
to: string;
|
|
47
|
+
/** Nested sub-items */
|
|
48
|
+
items?: Array<{
|
|
49
|
+
label: string;
|
|
50
|
+
to: string;
|
|
51
|
+
}>;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Declarative menu configuration.
|
|
55
|
+
*
|
|
56
|
+
* - `items` -> full replace (ignores remove/add/order)
|
|
57
|
+
* - `remove` + `add` + `order` -> patch mode (applied in that order)
|
|
58
|
+
*/
|
|
59
|
+
interface MenuConfig {
|
|
60
|
+
/** Full replace: provide all menu items */
|
|
61
|
+
items?: NavItemConfig[];
|
|
62
|
+
/** Remove items by path */
|
|
63
|
+
remove?: string[];
|
|
64
|
+
/** Add new items */
|
|
65
|
+
add?: NavItemConfig[];
|
|
66
|
+
/** Reorder: array of paths. Unlisted items appear at the end in original order. */
|
|
67
|
+
order?: string[];
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Function-based menu configuration.
|
|
71
|
+
* Receives the default core routes and returns the modified list.
|
|
72
|
+
*/
|
|
73
|
+
type MenuConfigFn = (routes: NavItemConfig[]) => NavItemConfig[];
|
|
74
|
+
|
|
75
|
+
export { type MedusaPresetOptions, type MenuConfig, type MenuConfigFn, type NavItemConfig, medusa };
|
package/dist/medusa.d.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { U as UnlockOptions } from './types-C3wWFKIK.js';
|
|
2
|
+
|
|
3
|
+
interface MedusaPresetOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Directory (or directories) containing override files.
|
|
6
|
+
* @default "./src/admin/overrides"
|
|
7
|
+
*/
|
|
8
|
+
overrides?: string | string[];
|
|
9
|
+
/**
|
|
10
|
+
* Enable debug logging.
|
|
11
|
+
* @default false
|
|
12
|
+
*/
|
|
13
|
+
debug?: boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Medusa dashboard preset for vite-plugin-unlock.
|
|
17
|
+
*
|
|
18
|
+
* Pre-configured to target `@medusajs/dashboard` with sensible defaults:
|
|
19
|
+
* - Entry redirect (unbundles dashboard source)
|
|
20
|
+
* - CSS redirect (avoids Tailwind reprocessing)
|
|
21
|
+
* - HMR boundaries for admin extensions
|
|
22
|
+
* - Menu patching via `menu.config.ts`
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* import { unlock } from "@unlockable/vite-plugin-unlock"
|
|
27
|
+
* import { medusa } from "@unlockable/vite-plugin-unlock/medusa"
|
|
28
|
+
*
|
|
29
|
+
* export default defineConfig({
|
|
30
|
+
* plugins: [unlock(medusa())],
|
|
31
|
+
* })
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
declare function medusa(options?: MedusaPresetOptions): UnlockOptions;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* A menu item configuration.
|
|
38
|
+
* Matches the shape of @medusajs/dashboard's internal nav items.
|
|
39
|
+
*/
|
|
40
|
+
interface NavItemConfig {
|
|
41
|
+
/** Icon — pass a component directly (e.g. ShoppingCart from @medusajs/icons) or a React element */
|
|
42
|
+
icon?: unknown;
|
|
43
|
+
/** Display label */
|
|
44
|
+
label: string;
|
|
45
|
+
/** Route path (e.g. "/orders") */
|
|
46
|
+
to: string;
|
|
47
|
+
/** Nested sub-items */
|
|
48
|
+
items?: Array<{
|
|
49
|
+
label: string;
|
|
50
|
+
to: string;
|
|
51
|
+
}>;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Declarative menu configuration.
|
|
55
|
+
*
|
|
56
|
+
* - `items` -> full replace (ignores remove/add/order)
|
|
57
|
+
* - `remove` + `add` + `order` -> patch mode (applied in that order)
|
|
58
|
+
*/
|
|
59
|
+
interface MenuConfig {
|
|
60
|
+
/** Full replace: provide all menu items */
|
|
61
|
+
items?: NavItemConfig[];
|
|
62
|
+
/** Remove items by path */
|
|
63
|
+
remove?: string[];
|
|
64
|
+
/** Add new items */
|
|
65
|
+
add?: NavItemConfig[];
|
|
66
|
+
/** Reorder: array of paths. Unlisted items appear at the end in original order. */
|
|
67
|
+
order?: string[];
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Function-based menu configuration.
|
|
71
|
+
* Receives the default core routes and returns the modified list.
|
|
72
|
+
*/
|
|
73
|
+
type MenuConfigFn = (routes: NavItemConfig[]) => NavItemConfig[];
|
|
74
|
+
|
|
75
|
+
export { type MedusaPresetOptions, type MenuConfig, type MenuConfigFn, type NavItemConfig, medusa };
|
package/dist/medusa.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// src/presets/menu-patch.ts
|
|
2
|
+
function patchMenuLayout(code, configPath) {
|
|
3
|
+
const coreMarker = "const coreRoutes = useCoreRoutes()";
|
|
4
|
+
const extMarker = 'const menuItems = getMenu("coreExtensions").filter((item) => !item.nested)';
|
|
5
|
+
if (!code.includes(coreMarker)) {
|
|
6
|
+
return code;
|
|
7
|
+
}
|
|
8
|
+
const safePath = configPath.replace(/\\/g, "/").replace(/"/g, '\\"');
|
|
9
|
+
const importLine = `import __menuConfig from "${safePath}";
|
|
10
|
+
import * as __React from "react";
|
|
11
|
+
import __i18n from "i18next";
|
|
12
|
+
`;
|
|
13
|
+
let patched = code.replace(
|
|
14
|
+
coreMarker,
|
|
15
|
+
"const coreRoutes = __applyMenuConfig(useCoreRoutes(), __menuConfig)"
|
|
16
|
+
);
|
|
17
|
+
if (patched.includes(extMarker)) {
|
|
18
|
+
patched = patched.replace(
|
|
19
|
+
extMarker,
|
|
20
|
+
'const menuItems = getMenu("coreExtensions").filter((item) => !item.nested).filter(function(item) { return !__getPromotedPaths(__menuConfig).includes(item.to) })'
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
const helperFn = `
|
|
24
|
+
function __toNavItems(list) {
|
|
25
|
+
return list.map(function(item) {
|
|
26
|
+
var icon = typeof item.icon === "function" || (item.icon && item.icon.render) ? __React.createElement(item.icon) : item.icon;
|
|
27
|
+
var label = __i18n.t(item.label);
|
|
28
|
+
var kids = item.items;
|
|
29
|
+
var translatedKids = kids && kids.length > 0 ? kids.map(function(k) { return { label: __i18n.t(k.label), to: k.to }; }) : undefined;
|
|
30
|
+
return { icon: icon, label: label, to: item.to, items: translatedKids };
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function __applyMenuConfig(routes, config) {
|
|
35
|
+
if (!config) return routes;
|
|
36
|
+
if (typeof config === "function") return __toNavItems(config(routes));
|
|
37
|
+
if (config.items) return __toNavItems(config.items);
|
|
38
|
+
let result = [...routes];
|
|
39
|
+
if (config.remove) {
|
|
40
|
+
result = result.filter(function(r) { return !config.remove.includes(r.to) });
|
|
41
|
+
}
|
|
42
|
+
if (config.add) {
|
|
43
|
+
result = result.concat(__toNavItems(config.add));
|
|
44
|
+
}
|
|
45
|
+
if (config.order) {
|
|
46
|
+
var ordered = [];
|
|
47
|
+
var rest = [].concat(result);
|
|
48
|
+
for (var k = 0; k < config.order.length; k++) {
|
|
49
|
+
var p = config.order[k];
|
|
50
|
+
var i = rest.findIndex(function(r) { return r.to === p });
|
|
51
|
+
if (i !== -1) ordered.push(rest.splice(i, 1)[0]);
|
|
52
|
+
}
|
|
53
|
+
result = ordered.concat(rest);
|
|
54
|
+
}
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function __getPromotedPaths(config) {
|
|
59
|
+
if (!config || typeof config === "function") return [];
|
|
60
|
+
var paths = [];
|
|
61
|
+
if (config.items) {
|
|
62
|
+
config.items.forEach(function(item) { paths.push(item.to) });
|
|
63
|
+
}
|
|
64
|
+
if (config.add) {
|
|
65
|
+
config.add.forEach(function(item) { paths.push(item.to) });
|
|
66
|
+
}
|
|
67
|
+
return paths;
|
|
68
|
+
}
|
|
69
|
+
`;
|
|
70
|
+
return importLine + patched + helperFn;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// src/presets/medusa.ts
|
|
74
|
+
function medusa(options) {
|
|
75
|
+
const {
|
|
76
|
+
overrides = "./src/admin/overrides",
|
|
77
|
+
debug = false
|
|
78
|
+
} = options ?? {};
|
|
79
|
+
return {
|
|
80
|
+
targets: [
|
|
81
|
+
{
|
|
82
|
+
package: "@medusajs/dashboard",
|
|
83
|
+
alias: "~dashboard",
|
|
84
|
+
entryRedirect: { from: "dist/app.mjs", to: "src/app.tsx" },
|
|
85
|
+
hmr: {
|
|
86
|
+
cssRedirect: { from: "./index.css", to: "../dist/app.css" },
|
|
87
|
+
entryBoundary: true
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
],
|
|
91
|
+
overrides,
|
|
92
|
+
debug,
|
|
93
|
+
hmrBoundaries: [
|
|
94
|
+
"defineRouteConfig",
|
|
95
|
+
"defineWidgetConfig"
|
|
96
|
+
],
|
|
97
|
+
patches: [
|
|
98
|
+
{
|
|
99
|
+
target: /main-layout\.tsx$/,
|
|
100
|
+
configFile: "menu.config",
|
|
101
|
+
apply: patchMenuLayout
|
|
102
|
+
}
|
|
103
|
+
]
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
export {
|
|
107
|
+
medusa
|
|
108
|
+
};
|
|
109
|
+
//# sourceMappingURL=medusa.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/presets/menu-patch.ts","../src/presets/medusa.ts"],"sourcesContent":["/**\n * Pure patch function for the Medusa dashboard sidebar menu.\n *\n * Transforms main-layout.tsx to inject a menu config import and\n * wrap useCoreRoutes() with __applyMenuConfig().\n *\n * Used as a `patches[].apply` function by vite-plugin-unlock.\n */\nexport function patchMenuLayout(code: string, configPath: string): string {\n const coreMarker = \"const coreRoutes = useCoreRoutes()\"\n const extMarker =\n 'const menuItems = getMenu(\"coreExtensions\").filter((item) => !item.nested)'\n\n if (!code.includes(coreMarker)) {\n return code\n }\n\n const safePath = configPath.replace(/\\\\/g, \"/\").replace(/\"/g, '\\\\\"')\n const importLine = `import __menuConfig from \"${safePath}\";\\nimport * as __React from \"react\";\\nimport __i18n from \"i18next\";\\n`\n\n let patched = code.replace(\n coreMarker,\n \"const coreRoutes = __applyMenuConfig(useCoreRoutes(), __menuConfig)\"\n )\n\n if (patched.includes(extMarker)) {\n patched = patched.replace(\n extMarker,\n 'const menuItems = getMenu(\"coreExtensions\").filter((item) => !item.nested).filter(function(item) { return !__getPromotedPaths(__menuConfig).includes(item.to) })'\n )\n }\n\n const helperFn = `\nfunction __toNavItems(list) {\n return list.map(function(item) {\n var icon = typeof item.icon === \"function\" || (item.icon && item.icon.render) ? __React.createElement(item.icon) : item.icon;\n var label = __i18n.t(item.label);\n var kids = item.items;\n var translatedKids = kids && kids.length > 0 ? kids.map(function(k) { return { label: __i18n.t(k.label), to: k.to }; }) : undefined;\n return { icon: icon, label: label, to: item.to, items: translatedKids };\n });\n}\n\nfunction __applyMenuConfig(routes, config) {\n if (!config) return routes;\n if (typeof config === \"function\") return __toNavItems(config(routes));\n if (config.items) return __toNavItems(config.items);\n let result = [...routes];\n if (config.remove) {\n result = result.filter(function(r) { return !config.remove.includes(r.to) });\n }\n if (config.add) {\n result = result.concat(__toNavItems(config.add));\n }\n if (config.order) {\n var ordered = [];\n var rest = [].concat(result);\n for (var k = 0; k < config.order.length; k++) {\n var p = config.order[k];\n var i = rest.findIndex(function(r) { return r.to === p });\n if (i !== -1) ordered.push(rest.splice(i, 1)[0]);\n }\n result = ordered.concat(rest);\n }\n return result;\n}\n\nfunction __getPromotedPaths(config) {\n if (!config || typeof config === \"function\") return [];\n var paths = [];\n if (config.items) {\n config.items.forEach(function(item) { paths.push(item.to) });\n }\n if (config.add) {\n config.add.forEach(function(item) { paths.push(item.to) });\n }\n return paths;\n}\n`\n\n return importLine + patched + helperFn\n}\n","import type { UnlockOptions } from \"../types\"\nimport { patchMenuLayout } from \"./menu-patch\"\n\nexport interface MedusaPresetOptions {\n /**\n * Directory (or directories) containing override files.\n * @default \"./src/admin/overrides\"\n */\n overrides?: string | string[]\n\n /**\n * Enable debug logging.\n * @default false\n */\n debug?: boolean\n}\n\n/**\n * Medusa dashboard preset for vite-plugin-unlock.\n *\n * Pre-configured to target `@medusajs/dashboard` with sensible defaults:\n * - Entry redirect (unbundles dashboard source)\n * - CSS redirect (avoids Tailwind reprocessing)\n * - HMR boundaries for admin extensions\n * - Menu patching via `menu.config.ts`\n *\n * @example\n * ```ts\n * import { unlock } from \"@unlockable/vite-plugin-unlock\"\n * import { medusa } from \"@unlockable/vite-plugin-unlock/medusa\"\n *\n * export default defineConfig({\n * plugins: [unlock(medusa())],\n * })\n * ```\n */\nexport function medusa(options?: MedusaPresetOptions): UnlockOptions {\n const {\n overrides = \"./src/admin/overrides\",\n debug = false,\n } = options ?? {}\n\n return {\n targets: [\n {\n package: \"@medusajs/dashboard\",\n alias: \"~dashboard\",\n entryRedirect: { from: \"dist/app.mjs\", to: \"src/app.tsx\" },\n hmr: {\n cssRedirect: { from: \"./index.css\", to: \"../dist/app.css\" },\n entryBoundary: true,\n },\n },\n ],\n overrides,\n debug,\n hmrBoundaries: [\n \"defineRouteConfig\",\n \"defineWidgetConfig\",\n ],\n patches: [\n {\n target: /main-layout\\.tsx$/,\n configFile: \"menu.config\",\n apply: patchMenuLayout,\n },\n ],\n }\n}\n"],"mappings":";AAQO,SAAS,gBAAgB,MAAc,YAA4B;AACxE,QAAM,aAAa;AACnB,QAAM,YACJ;AAEF,MAAI,CAAC,KAAK,SAAS,UAAU,GAAG;AAC9B,WAAO;AAAA,EACT;AAEA,QAAM,WAAW,WAAW,QAAQ,OAAO,GAAG,EAAE,QAAQ,MAAM,KAAK;AACnE,QAAM,aAAa,6BAA6B,QAAQ;AAAA;AAAA;AAAA;AAExD,MAAI,UAAU,KAAK;AAAA,IACjB;AAAA,IACA;AAAA,EACF;AAEA,MAAI,QAAQ,SAAS,SAAS,GAAG;AAC/B,cAAU,QAAQ;AAAA,MAChB;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,QAAM,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgDjB,SAAO,aAAa,UAAU;AAChC;;;AC7CO,SAAS,OAAO,SAA8C;AACnE,QAAM;AAAA,IACJ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,IAAI,WAAW,CAAC;AAEhB,SAAO;AAAA,IACL,SAAS;AAAA,MACP;AAAA,QACE,SAAS;AAAA,QACT,OAAO;AAAA,QACP,eAAe,EAAE,MAAM,gBAAgB,IAAI,cAAc;AAAA,QACzD,KAAK;AAAA,UACH,aAAa,EAAE,MAAM,eAAe,IAAI,kBAAkB;AAAA,UAC1D,eAAe;AAAA,QACjB;AAAA,MACF;AAAA,IACF;AAAA,IACA;AAAA,IACA;AAAA,IACA,eAAe;AAAA,MACb;AAAA,MACA;AAAA,IACF;AAAA,IACA,SAAS;AAAA,MACP;AAAA,QACE,QAAQ;AAAA,QACR,YAAY;AAAA,QACZ,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|