@murumets-ee/create 0.4.6 → 0.5.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/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/scaffold.ts"],"mappings":";KAAY,QAAA;AAAA,UAEK,cAAA;EACf,IAAA;EACA,QAAA,EAAU,QAAA;EACV,WAAA;AAAA;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/scaffold.ts"],"mappings":";KAAY,QAAA;AAAA,UAEK,cAAA;EACf,IAAA;EACA,QAAA,EAAU,QAAA;EACV,WAAA;AAAA;;;KCOU,gBAAA,IAAoB,OAAA;AAAA,iBAoBV,QAAA,CACpB,OAAA,EAAS,cAAA,EACT,UAAA,GAAa,gBAAA,GACZ,OAAA"}
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["tarExtract"],"sources":["../src/utils.ts","../src/scaffold.ts"],"sourcesContent":["import { execSync } from 'node:child_process'\nimport { appendFile, writeFile as fsWriteFile, mkdir, readFile, rm } from 'node:fs/promises'\nimport { dirname, join } from 'node:path'\n\n/** Write a file, creating parent directories as needed */\nexport async function writeFile(filePath: string, content: string): Promise<void> {\n await mkdir(dirname(filePath), { recursive: true })\n await fsWriteFile(filePath, content, 'utf-8')\n}\n\n/** Read and parse package.json, merge in new deps/scripts, write back */\nexport async function mergePackageJson(\n pkgPath: string,\n merge: {\n type?: string\n dependencies?: Record<string, string>\n devDependencies?: Record<string, string>\n scripts?: Record<string, string>\n },\n): Promise<void> {\n const raw = await readFile(pkgPath, 'utf-8')\n const pkg = JSON.parse(raw)\n\n if (merge.type) {\n pkg.type = merge.type\n }\n if (merge.dependencies) {\n pkg.dependencies = { ...pkg.dependencies, ...merge.dependencies }\n }\n if (merge.devDependencies) {\n pkg.devDependencies = { ...pkg.devDependencies, ...merge.devDependencies }\n }\n if (merge.scripts) {\n pkg.scripts = { ...pkg.scripts, ...merge.scripts }\n }\n\n await fsWriteFile(pkgPath, `${JSON.stringify(pkg, null, 2)}\\n`, 'utf-8')\n}\n\n/** Delete files (silently ignores missing) */\nexport async function deleteFiles(basePath: string, files: string[]): Promise<void> {\n for (const file of files) {\n await rm(join(basePath, file), { force: true, recursive: true })\n }\n}\n\n/** Run a shell command synchronously */\nexport function runCommand(cmd: string, options?: { cwd?: string }): string {\n return execSync(cmd, {\n cwd: options?.cwd,\n stdio: 'pipe',\n encoding: 'utf-8',\n })\n}\n\n/** Append content to a file */\nexport async function appendToFile(filePath: string, content: string): Promise<void> {\n await appendFile(filePath, content, 'utf-8')\n}\n","import { existsSync, readFileSync } from 'node:fs'\nimport { readdir, readFile, rm, writeFile } from 'node:fs/promises'\nimport { dirname, join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { extract as tarExtract } from 'tar'\nimport type { ProjectOptions } from './types'\nimport { deleteFiles, runCommand } from './utils'\nimport { CREATE_NEXT_APP_VERSION } from './versions'\n\nexport type ProgressCallback = (message: string) => void\n\n// File extensions eligible for token replacement during scaffolding.\nconst TOKENIZABLE_EXTENSIONS = new Set<string>([\n '.ts',\n '.tsx',\n '.mts',\n '.json',\n '.md',\n '.yml',\n '.yaml',\n '.css',\n '.html',\n])\n\nconst TOKENIZABLE_BASENAMES = new Set<string>(['.env.example', 'Dockerfile', '.gitignore'])\n\nconst TOKEN_PROJECT_NAME = '__PROJECT_NAME__'\nconst TOKEN_PROJECT_NAME_TITLE = '__PROJECT_NAME_TITLE__'\n\nexport async function scaffold(\n options: ProjectOptions,\n onProgress?: ProgressCallback,\n): Promise<void> {\n await scaffoldSingle(options, onProgress)\n}\n\n// ---------------------------------------------------------------------------\n// scaffoldSingle — extract a prebuilt tarball, then tokenize.\n// ---------------------------------------------------------------------------\n\nasync function scaffoldSingle(\n options: ProjectOptions,\n onProgress?: ProgressCallback,\n): Promise<void> {\n const projectDir = join(process.cwd(), options.name)\n\n // 1. Run create-next-app to get an authoritative Next.js skeleton\n // (lockfile, ESLint config, default tsconfig, etc.)\n onProgress?.('Creating Next.js app...')\n runCommand(\n `pnpm create next-app@${CREATE_NEXT_APP_VERSION} ${options.name} ` +\n '--typescript --tailwind --no-eslint --app --no-src-dir --turbopack --react-compiler --skip-install --import-alias \"@/*\"',\n )\n\n // 2. Cleanup default Next.js artifacts that the template will replace\n await deleteFiles(projectDir, [\n 'app/page.tsx',\n 'app/page.module.css',\n 'app/fonts',\n 'app/globals.css',\n 'README.md',\n ])\n\n // 3. Extract the prebuilt template tarball over the project directory.\n // Both demo and blank use the same tarball — blank strips content after.\n onProgress?.('Extracting template...')\n await extractTemplate(projectDir)\n\n // 4. For blank template: remove demo entities, seeds, and demo routes\n if (options.template === 'blank') {\n onProgress?.('Stripping demo content for blank template...')\n await stripForBlank(projectDir)\n }\n\n // 5. Replace tokens in tokenizable files\n onProgress?.('Personalizing template...')\n await tokenizeProject(projectDir, options.name)\n\n // 6. Install deps\n if (options.installDeps) {\n onProgress?.('Installing dependencies...')\n runCommand('pnpm install', { cwd: projectDir })\n }\n}\n\n// ---------------------------------------------------------------------------\n// Tarball extraction\n// ---------------------------------------------------------------------------\n\nasync function extractTemplate(destDir: string): Promise<void> {\n const tarball = resolveTemplateTarball()\n if (!existsSync(tarball)) {\n throw new Error(\n `Template tarball missing: ${tarball}\\n` +\n `Run \\`pnpm --filter @murumets-ee/create build:templates\\` first.`,\n )\n }\n await tarExtract({ file: tarball, cwd: destDir })\n}\n\n/**\n * Locate the template tarball relative to the published `@murumets-ee/create`\n * package root.\n *\n * Anchors on the package's own `package.json` (matched by `name`) so a parent\n * project that happens to have a `templates/` directory at a higher path\n * can't hijack the resolution. This is a defense-in-depth measure against\n * template-substitution attacks where `create-lumi` is invoked from inside\n * a malicious project layout.\n */\nfunction resolveTemplateTarball(): string {\n const here = dirname(fileURLToPath(import.meta.url))\n let dir = here\n for (let i = 0; i < 5; i++) {\n const pkgJsonPath = join(dir, 'package.json')\n if (existsSync(pkgJsonPath)) {\n try {\n const pkg = JSON.parse(readFileSync(pkgJsonPath, 'utf-8')) as { name?: string }\n if (pkg.name === '@murumets-ee/create') {\n return join(dir, 'templates', 'template-demo.tar.gz')\n }\n } catch {\n // Malformed package.json — keep walking.\n }\n }\n const parent = dirname(dir)\n if (parent === dir) break // hit filesystem root\n dir = parent\n }\n throw new Error(\n `Could not find @murumets-ee/create package root above ${here}. ` +\n `template-demo.tar.gz cannot be located.`,\n )\n}\n\n// ---------------------------------------------------------------------------\n// Blank template: post-extract cleanup\n// ---------------------------------------------------------------------------\n\n/** admin-config.ts for blank template — plugin entities only, no project entities. */\nconst BLANK_ADMIN_CONFIG = `/**\n * Shared admin configuration — entities + route resource metadata.\n *\n * Add your own entities to the arrays below after defining them in entities/.\n */\n\nimport { Media } from '@murumets-ee/media'\nimport {\n Ticket,\n TicketMessage,\n TicketAttachment,\n Department,\n TicketTag,\n} from '@murumets-ee/ticketing'\n\n/** All content entities registered in the admin API. */\nexport const allEntities = [\n Media,\n Ticket, TicketMessage, TicketAttachment, Department, TicketTag,\n]\n\n/** Taxonomy vocabularies. */\nexport const taxonomyVocabularies = {}\n\n/** Entities exposed via generic CRUD handler (subset of allEntities). */\nexport const crudEntities = [\n Media,\n Ticket, TicketMessage, TicketAttachment,\n]\n\n/**\n * Plugin route resource declarations.\n */\nexport const pluginResources: { resource: string; actions: readonly string[] }[] = [\n { resource: 'storage', actions: ['view', 'create', 'update', 'delete'] },\n { resource: 'settings', actions: ['view', 'update'] },\n { resource: 'audit-logs', actions: ['view'] },\n { resource: 'permissions', actions: ['view', 'create', 'update', 'delete'] },\n { resource: 'ticketing', actions: ['view', 'create', 'update', 'delete'] },\n]\n`\n\n/**\n * Strip demo content from a freshly extracted template to produce the\n * \"blank\" variant:\n *\n * 1. Delete entity files (keep `entities/index.ts` with empty exports)\n * 2. Delete seed files (keep `seeds/index.ts` with empty array)\n * 3. Delete `app/[locale]/(shell)/demos/` entirely\n * 4. Remove the demosGroup from `app/admin-layout.tsx`\n * 5. Rewrite toolkit.config.ts, admin-config.ts, content API route,\n * and admin dashboard to not reference project entities\n */\nasync function stripForBlank(projectDir: string): Promise<void> {\n // 1. Entities — delete all .ts files except index.ts, rewrite index to empty\n const entitiesDir = join(projectDir, 'entities')\n if (existsSync(entitiesDir)) {\n const files = await readdir(entitiesDir)\n for (const f of files) {\n if (f !== 'index.ts') await rm(join(entitiesDir, f), { force: true })\n }\n await writeFile(\n join(entitiesDir, 'index.ts'),\n '// Add your entities here and export them.\\n' +\n '// See https://github.com/murumets-ee/lumi-cms for documentation.\\n' +\n '\\n' +\n 'export const projectEntities = [] as const\\n' +\n 'export const projectTaxonomies = [] as const\\n',\n 'utf-8',\n )\n }\n\n // 2. Seeds — delete all .ts files except index.ts, rewrite index to empty\n const seedsDir = join(projectDir, 'seeds')\n if (existsSync(seedsDir)) {\n const files = await readdir(seedsDir)\n for (const f of files) {\n if (f !== 'index.ts') await rm(join(seedsDir, f), { force: true })\n }\n await writeFile(\n join(seedsDir, 'index.ts'),\n \"import type { AdminSeeder } from '@murumets-ee/admin-ui/pages'\\n\\n\" +\n 'export const projectSeeders: AdminSeeder[] = []\\n',\n 'utf-8',\n )\n }\n\n // 3. Demo routes\n const demosDir = join(projectDir, 'app', '[locale]', '(shell)', 'demos')\n await rm(demosDir, { recursive: true, force: true })\n\n // 4. Remove demosGroup from admin-layout.tsx (case-insensitive — comment says \"demo\")\n const layoutPath = join(projectDir, 'app', 'admin-layout.tsx')\n if (existsSync(layoutPath)) {\n let content = await readFile(layoutPath, 'utf-8')\n content = content.replace(\n /\\n\\s*\\/\\/.*demo.*[\\s\\S]*?const demosGroup[\\s\\S]*?\\}\\n/i,\n '\\n',\n )\n content = content.replace(', demosGroup', '')\n // Remove icon imports only used by demosGroup\n content = content.replace(/\\s*GalleryHorizontalEnd,\\n/, '\\n')\n content = content.replace(/\\s*HardDrive,\\n/, '\\n')\n content = content.replace(/\\s*Palette,\\n/, '\\n')\n await writeFile(layoutPath, content, 'utf-8')\n }\n\n // 5. Rewrite toolkit.config.ts — use projectEntities instead of named imports\n const configPath = join(projectDir, 'toolkit.config.ts')\n if (existsSync(configPath)) {\n let content = await readFile(configPath, 'utf-8')\n content = content.replace(\n /import \\{[^}]+\\} from '\\.\\/entities'/,\n \"import { projectEntities } from './entities'\",\n )\n content = content.replace(\n /entities: \\[[^\\]]+\\]/,\n 'entities: [...projectEntities]',\n )\n await writeFile(configPath, content, 'utf-8')\n }\n\n // 6. Rewrite lib/admin-config.ts — no project entities, only plugin entities\n const adminConfigPath = join(projectDir, 'lib', 'admin-config.ts')\n if (existsSync(adminConfigPath)) {\n await writeFile(\n adminConfigPath,\n BLANK_ADMIN_CONFIG,\n 'utf-8',\n )\n }\n\n // 7. Rewrite content API route — empty entity whitelist\n const contentRoutePath = join(projectDir, 'app', 'api', 'content', '[...path]', 'route.ts')\n if (existsSync(contentRoutePath)) {\n let content = await readFile(contentRoutePath, 'utf-8')\n content = content.replace(/import \\{[^}]+\\} from '@\\/entities'\\n/, '')\n content = content.replace(/entities: \\[[^\\]]+\\]/, 'entities: []')\n await writeFile(contentRoutePath, content, 'utf-8')\n }\n\n // 8. Replace admin dashboard page with auto-admin DashboardPage\n const dashboardPath = join(projectDir, 'app', '[locale]', '(shell)', 'admin', 'page.tsx')\n if (existsSync(dashboardPath)) {\n await writeFile(\n dashboardPath,\n \"import { pages } from '@/lib/admin-pages'\\n\\nexport default pages.DashboardPage\\n\",\n 'utf-8',\n )\n }\n}\n\n// ---------------------------------------------------------------------------\n// Token replacement\n// ---------------------------------------------------------------------------\n\n/**\n * Walk the project directory and replace `__PROJECT_NAME__` /\n * `__PROJECT_NAME_TITLE__` tokens in eligible text files.\n *\n * Binary files (images, fonts) and `node_modules` / `.next` are skipped\n * to avoid corruption and pointless work.\n */\nasync function tokenizeProject(projectDir: string, projectName: string): Promise<void> {\n const titleName = toTitleCase(projectName)\n for await (const filePath of walkFiles(projectDir)) {\n if (!isTokenizable(filePath)) continue\n const original = await readFile(filePath, 'utf-8')\n if (!original.includes(TOKEN_PROJECT_NAME) && !original.includes(TOKEN_PROJECT_NAME_TITLE)) {\n continue\n }\n const replaced = original\n .split(TOKEN_PROJECT_NAME_TITLE)\n .join(titleName)\n .split(TOKEN_PROJECT_NAME)\n .join(projectName)\n await writeFile(filePath, replaced, 'utf-8')\n }\n}\n\n/**\n * Directories the tokenizer must skip.\n */\nconst SKIP_DIRS = new Set<string>([\n 'node_modules',\n 'dist',\n 'build',\n 'coverage',\n 'out',\n])\n\nasync function* walkFiles(root: string): AsyncGenerator<string> {\n const entries = await readdir(root, { withFileTypes: true })\n for (const entry of entries) {\n const full = join(root, entry.name)\n if (entry.isDirectory()) {\n if (SKIP_DIRS.has(entry.name) || entry.name.startsWith('.')) continue\n yield* walkFiles(full)\n } else if (entry.isFile()) {\n yield full\n }\n }\n}\n\nfunction isTokenizable(filePath: string): boolean {\n const slash = filePath.lastIndexOf('/')\n const basename = slash >= 0 ? filePath.slice(slash + 1) : filePath\n if (TOKENIZABLE_BASENAMES.has(basename)) return true\n const dot = basename.lastIndexOf('.')\n if (dot < 0) return false\n const ext = basename.slice(dot)\n return TOKENIZABLE_EXTENSIONS.has(ext)\n}\n\nfunction toTitleCase(name: string): string {\n return name\n .split(/[-_\\s]+/)\n .filter(Boolean)\n .map((part) => part[0]?.toUpperCase() + part.slice(1))\n .join(' ')\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["tarExtract"],"sources":["../src/utils.ts","../src/scaffold.ts"],"sourcesContent":["import { execSync } from 'node:child_process'\nimport { appendFile, writeFile as fsWriteFile, mkdir, readFile, rm } from 'node:fs/promises'\nimport { dirname, join } from 'node:path'\n\n/** Write a file, creating parent directories as needed */\nexport async function writeFile(filePath: string, content: string): Promise<void> {\n await mkdir(dirname(filePath), { recursive: true })\n await fsWriteFile(filePath, content, 'utf-8')\n}\n\n/** Read and parse package.json, merge in new deps/scripts, write back */\nexport async function mergePackageJson(\n pkgPath: string,\n merge: {\n type?: string\n dependencies?: Record<string, string>\n devDependencies?: Record<string, string>\n scripts?: Record<string, string>\n },\n): Promise<void> {\n const raw = await readFile(pkgPath, 'utf-8')\n const pkg = JSON.parse(raw)\n\n if (merge.type) {\n pkg.type = merge.type\n }\n if (merge.dependencies) {\n pkg.dependencies = { ...pkg.dependencies, ...merge.dependencies }\n }\n if (merge.devDependencies) {\n pkg.devDependencies = { ...pkg.devDependencies, ...merge.devDependencies }\n }\n if (merge.scripts) {\n pkg.scripts = { ...pkg.scripts, ...merge.scripts }\n }\n\n await fsWriteFile(pkgPath, `${JSON.stringify(pkg, null, 2)}\\n`, 'utf-8')\n}\n\n/** Delete files (silently ignores missing) */\nexport async function deleteFiles(basePath: string, files: string[]): Promise<void> {\n for (const file of files) {\n await rm(join(basePath, file), { force: true, recursive: true })\n }\n}\n\n/** Run a shell command synchronously */\nexport function runCommand(cmd: string, options?: { cwd?: string }): string {\n return execSync(cmd, {\n cwd: options?.cwd,\n stdio: 'pipe',\n encoding: 'utf-8',\n })\n}\n\n/** Append content to a file */\nexport async function appendToFile(filePath: string, content: string): Promise<void> {\n await appendFile(filePath, content, 'utf-8')\n}\n","import { existsSync, readFileSync } from 'node:fs'\nimport { readdir, readFile, rm, writeFile } from 'node:fs/promises'\nimport { dirname, join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { extract as tarExtract } from 'tar'\nimport type { ProjectOptions } from './types'\nimport { deleteFiles, runCommand } from './utils'\n\n// Major Next.js version passed to `pnpm create next-app@<ver>`.\n// Bumped when the toolkit as a whole moves to a new Next major.\nconst CREATE_NEXT_APP_VERSION = '16'\n\nexport type ProgressCallback = (message: string) => void\n\n// File extensions eligible for token replacement during scaffolding.\nconst TOKENIZABLE_EXTENSIONS = new Set<string>([\n '.ts',\n '.tsx',\n '.mts',\n '.json',\n '.md',\n '.yml',\n '.yaml',\n '.css',\n '.html',\n])\n\nconst TOKENIZABLE_BASENAMES = new Set<string>(['.env.example', 'Dockerfile', '.gitignore'])\n\nconst TOKEN_PROJECT_NAME = '__PROJECT_NAME__'\nconst TOKEN_PROJECT_NAME_TITLE = '__PROJECT_NAME_TITLE__'\n\nexport async function scaffold(\n options: ProjectOptions,\n onProgress?: ProgressCallback,\n): Promise<void> {\n await scaffoldSingle(options, onProgress)\n}\n\n// ---------------------------------------------------------------------------\n// scaffoldSingle — extract a prebuilt tarball, then tokenize.\n// ---------------------------------------------------------------------------\n\nasync function scaffoldSingle(\n options: ProjectOptions,\n onProgress?: ProgressCallback,\n): Promise<void> {\n const projectDir = join(process.cwd(), options.name)\n\n // 1. Run create-next-app to get an authoritative Next.js skeleton\n // (lockfile, ESLint config, default tsconfig, etc.)\n onProgress?.('Creating Next.js app...')\n runCommand(\n `pnpm create next-app@${CREATE_NEXT_APP_VERSION} ${options.name} ` +\n '--typescript --tailwind --no-eslint --app --no-src-dir --turbopack --react-compiler --skip-install --import-alias \"@/*\"',\n )\n\n // 2. Cleanup default Next.js artifacts that the template will replace\n await deleteFiles(projectDir, [\n 'app/page.tsx',\n 'app/page.module.css',\n 'app/fonts',\n 'app/globals.css',\n 'README.md',\n ])\n\n // 3. Extract the prebuilt template tarball over the project directory.\n // Both demo and blank use the same tarball — blank strips content after.\n onProgress?.('Extracting template...')\n await extractTemplate(projectDir)\n\n // 4. For blank template: remove demo entities, seeds, and demo routes\n if (options.template === 'blank') {\n onProgress?.('Stripping demo content for blank template...')\n await stripForBlank(projectDir)\n }\n\n // 5. Replace tokens in tokenizable files\n onProgress?.('Personalizing template...')\n await tokenizeProject(projectDir, options.name)\n\n // 6. Install deps\n if (options.installDeps) {\n onProgress?.('Installing dependencies...')\n runCommand('pnpm install', { cwd: projectDir })\n }\n}\n\n// ---------------------------------------------------------------------------\n// Tarball extraction\n// ---------------------------------------------------------------------------\n\nasync function extractTemplate(destDir: string): Promise<void> {\n const tarball = resolveTemplateTarball()\n if (!existsSync(tarball)) {\n throw new Error(\n `Template tarball missing: ${tarball}\\n` +\n `Run \\`pnpm --filter @murumets-ee/create build:templates\\` first.`,\n )\n }\n await tarExtract({ file: tarball, cwd: destDir })\n}\n\n/**\n * Locate the template tarball relative to the published `@murumets-ee/create`\n * package root.\n *\n * Anchors on the package's own `package.json` (matched by `name`) so a parent\n * project that happens to have a `templates/` directory at a higher path\n * can't hijack the resolution. This is a defense-in-depth measure against\n * template-substitution attacks where `create-lumi` is invoked from inside\n * a malicious project layout.\n */\nfunction resolveTemplateTarball(): string {\n const here = dirname(fileURLToPath(import.meta.url))\n let dir = here\n for (let i = 0; i < 5; i++) {\n const pkgJsonPath = join(dir, 'package.json')\n if (existsSync(pkgJsonPath)) {\n try {\n const pkg = JSON.parse(readFileSync(pkgJsonPath, 'utf-8')) as { name?: string }\n if (pkg.name === '@murumets-ee/create') {\n return join(dir, 'templates', 'template-demo.tar.gz')\n }\n } catch {\n // Malformed package.json — keep walking.\n }\n }\n const parent = dirname(dir)\n if (parent === dir) break // hit filesystem root\n dir = parent\n }\n throw new Error(\n `Could not find @murumets-ee/create package root above ${here}. ` +\n `template-demo.tar.gz cannot be located.`,\n )\n}\n\n// ---------------------------------------------------------------------------\n// Blank template: post-extract cleanup\n// ---------------------------------------------------------------------------\n\n/** admin-config.ts for blank template — plugin entities only, no project entities. */\nconst BLANK_ADMIN_CONFIG = `/**\n * Shared admin configuration — entities + route resource metadata.\n *\n * Add your own entities to the arrays below after defining them in entities/.\n */\n\nimport { Media } from '@murumets-ee/media'\nimport {\n Ticket,\n TicketMessage,\n TicketAttachment,\n Department,\n TicketTag,\n} from '@murumets-ee/ticketing'\n\n/** All content entities registered in the admin API. */\nexport const allEntities = [\n Media,\n Ticket, TicketMessage, TicketAttachment, Department, TicketTag,\n]\n\n/** Taxonomy vocabularies. */\nexport const taxonomyVocabularies = {}\n\n/** Entities exposed via generic CRUD handler (subset of allEntities). */\nexport const crudEntities = [\n Media,\n Ticket, TicketMessage, TicketAttachment,\n]\n\n/**\n * Plugin route resource declarations.\n */\nexport const pluginResources: { resource: string; actions: readonly string[] }[] = [\n { resource: 'storage', actions: ['view', 'create', 'update', 'delete'] },\n { resource: 'settings', actions: ['view', 'update'] },\n { resource: 'audit-logs', actions: ['view'] },\n { resource: 'permissions', actions: ['view', 'create', 'update', 'delete'] },\n { resource: 'ticketing', actions: ['view', 'create', 'update', 'delete'] },\n]\n`\n\n/**\n * Strip demo content from a freshly extracted template to produce the\n * \"blank\" variant:\n *\n * 1. Delete entity files (keep `entities/index.ts` with empty exports)\n * 2. Delete seed files (keep `seeds/index.ts` with empty array)\n * 3. Delete `app/[locale]/(shell)/demos/` entirely\n * 4. Remove the demosGroup from `app/admin-layout.tsx`\n * 5. Rewrite toolkit.config.ts, admin-config.ts, content API route,\n * and admin dashboard to not reference project entities\n */\nasync function stripForBlank(projectDir: string): Promise<void> {\n // 1. Entities — delete all .ts files except index.ts, rewrite index to empty\n const entitiesDir = join(projectDir, 'entities')\n if (existsSync(entitiesDir)) {\n const files = await readdir(entitiesDir)\n for (const f of files) {\n if (f !== 'index.ts') await rm(join(entitiesDir, f), { force: true })\n }\n await writeFile(\n join(entitiesDir, 'index.ts'),\n '// Add your entities here and export them.\\n' +\n '// See https://github.com/murumets-ee/lumi-cms for documentation.\\n' +\n '\\n' +\n 'export const projectEntities = [] as const\\n' +\n 'export const projectTaxonomies = [] as const\\n',\n 'utf-8',\n )\n }\n\n // 2. Seeds — delete all .ts files except index.ts, rewrite index to empty\n const seedsDir = join(projectDir, 'seeds')\n if (existsSync(seedsDir)) {\n const files = await readdir(seedsDir)\n for (const f of files) {\n if (f !== 'index.ts') await rm(join(seedsDir, f), { force: true })\n }\n await writeFile(\n join(seedsDir, 'index.ts'),\n \"import type { AdminSeeder } from '@murumets-ee/admin-ui/pages'\\n\\n\" +\n 'export const projectSeeders: AdminSeeder[] = []\\n',\n 'utf-8',\n )\n }\n\n // 3. Demo routes\n const demosDir = join(projectDir, 'app', '[locale]', '(shell)', 'demos')\n await rm(demosDir, { recursive: true, force: true })\n\n // 4. Remove demosGroup from admin-layout.tsx (case-insensitive — comment says \"demo\")\n const layoutPath = join(projectDir, 'app', 'admin-layout.tsx')\n if (existsSync(layoutPath)) {\n let content = await readFile(layoutPath, 'utf-8')\n content = content.replace(\n /\\n\\s*\\/\\/.*demo.*[\\s\\S]*?const demosGroup[\\s\\S]*?\\}\\n/i,\n '\\n',\n )\n content = content.replace(', demosGroup', '')\n // Remove icon imports only used by demosGroup\n content = content.replace(/\\s*GalleryHorizontalEnd,\\n/, '\\n')\n content = content.replace(/\\s*HardDrive,\\n/, '\\n')\n content = content.replace(/\\s*Palette,\\n/, '\\n')\n await writeFile(layoutPath, content, 'utf-8')\n }\n\n // 5. Rewrite toolkit.config.ts — use projectEntities instead of named imports\n const configPath = join(projectDir, 'toolkit.config.ts')\n if (existsSync(configPath)) {\n let content = await readFile(configPath, 'utf-8')\n content = content.replace(\n /import \\{[^}]+\\} from '\\.\\/entities'/,\n \"import { projectEntities } from './entities'\",\n )\n content = content.replace(\n /entities: \\[[^\\]]+\\]/,\n 'entities: [...projectEntities]',\n )\n await writeFile(configPath, content, 'utf-8')\n }\n\n // 6. Rewrite lib/admin-config.ts — no project entities, only plugin entities\n const adminConfigPath = join(projectDir, 'lib', 'admin-config.ts')\n if (existsSync(adminConfigPath)) {\n await writeFile(\n adminConfigPath,\n BLANK_ADMIN_CONFIG,\n 'utf-8',\n )\n }\n\n // 7. Rewrite content API route — empty entity whitelist\n const contentRoutePath = join(projectDir, 'app', 'api', 'content', '[...path]', 'route.ts')\n if (existsSync(contentRoutePath)) {\n let content = await readFile(contentRoutePath, 'utf-8')\n content = content.replace(/import \\{[^}]+\\} from '@\\/entities'\\n/, '')\n content = content.replace(/entities: \\[[^\\]]+\\]/, 'entities: []')\n await writeFile(contentRoutePath, content, 'utf-8')\n }\n\n // 8. Replace admin dashboard page with auto-admin DashboardPage\n const dashboardPath = join(projectDir, 'app', '[locale]', '(shell)', 'admin', 'page.tsx')\n if (existsSync(dashboardPath)) {\n await writeFile(\n dashboardPath,\n \"import { pages } from '@/lib/admin-pages'\\n\\nexport default pages.DashboardPage\\n\",\n 'utf-8',\n )\n }\n}\n\n// ---------------------------------------------------------------------------\n// Token replacement\n// ---------------------------------------------------------------------------\n\n/**\n * Walk the project directory and replace `__PROJECT_NAME__` /\n * `__PROJECT_NAME_TITLE__` tokens in eligible text files.\n *\n * Binary files (images, fonts) and `node_modules` / `.next` are skipped\n * to avoid corruption and pointless work.\n */\nasync function tokenizeProject(projectDir: string, projectName: string): Promise<void> {\n const titleName = toTitleCase(projectName)\n for await (const filePath of walkFiles(projectDir)) {\n if (!isTokenizable(filePath)) continue\n const original = await readFile(filePath, 'utf-8')\n if (!original.includes(TOKEN_PROJECT_NAME) && !original.includes(TOKEN_PROJECT_NAME_TITLE)) {\n continue\n }\n const replaced = original\n .split(TOKEN_PROJECT_NAME_TITLE)\n .join(titleName)\n .split(TOKEN_PROJECT_NAME)\n .join(projectName)\n await writeFile(filePath, replaced, 'utf-8')\n }\n}\n\n/**\n * Directories the tokenizer must skip.\n */\nconst SKIP_DIRS = new Set<string>([\n 'node_modules',\n 'dist',\n 'build',\n 'coverage',\n 'out',\n])\n\nasync function* walkFiles(root: string): AsyncGenerator<string> {\n const entries = await readdir(root, { withFileTypes: true })\n for (const entry of entries) {\n const full = join(root, entry.name)\n if (entry.isDirectory()) {\n if (SKIP_DIRS.has(entry.name) || entry.name.startsWith('.')) continue\n yield* walkFiles(full)\n } else if (entry.isFile()) {\n yield full\n }\n }\n}\n\nfunction isTokenizable(filePath: string): boolean {\n const slash = filePath.lastIndexOf('/')\n const basename = slash >= 0 ? filePath.slice(slash + 1) : filePath\n if (TOKENIZABLE_BASENAMES.has(basename)) return true\n const dot = basename.lastIndexOf('.')\n if (dot < 0) return false\n const ext = basename.slice(dot)\n return TOKENIZABLE_EXTENSIONS.has(ext)\n}\n\nfunction toTitleCase(name: string): string {\n return name\n .split(/[-_\\s]+/)\n .filter(Boolean)\n .map((part) => part[0]?.toUpperCase() + part.slice(1))\n .join(' ')\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@murumets-ee/create",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"license": "Elastic-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -25,13 +25,13 @@
|
|
|
25
25
|
"@types/node": "^20",
|
|
26
26
|
"@types/tar": "^6.1.13",
|
|
27
27
|
"tsdown": "^0.21.7",
|
|
28
|
-
"tsx": "^4.
|
|
28
|
+
"tsx": "^4.21.0",
|
|
29
29
|
"typescript": "^5.7.3"
|
|
30
30
|
},
|
|
31
31
|
"scripts": {
|
|
32
|
-
"build": "tsdown && tsx scripts/build-templates.ts",
|
|
32
|
+
"build": "node ../../scripts/verify-catalog-sync.mjs && tsdown && tsx scripts/build-templates.ts",
|
|
33
33
|
"build:code": "tsdown",
|
|
34
|
-
"build:templates": "tsx scripts/build-templates.ts",
|
|
34
|
+
"build:templates": "node ../../scripts/verify-catalog-sync.mjs && tsx scripts/build-templates.ts",
|
|
35
35
|
"dev": "tsdown --watch",
|
|
36
36
|
"test": "vitest"
|
|
37
37
|
}
|
|
Binary file
|