diff --git a/packages/create-gen-app-test/src/__tests__/__snapshots__/cached-template.test.ts.snap b/packages/create-gen-app-test/src/__tests__/__snapshots__/cached-template.test.ts.snap index 3fbeabc..616b62b 100644 --- a/packages/create-gen-app-test/src/__tests__/__snapshots__/cached-template.test.ts.snap +++ b/packages/create-gen-app-test/src/__tests__/__snapshots__/cached-template.test.ts.snap @@ -2,7 +2,6 @@ exports[`cached template integration tests first clone with variable replacement should snapshot created directory structure 1`] = ` [ - ".boilerplate.json", "LICENSE", "README.md", "__tests__/", @@ -39,7 +38,7 @@ exports[`cached template integration tests first clone with variable replacement "scripts": { "lint": "eslint . --fix", "test": "jest", - "test:watch": "jest --watch", + "test:watch": "jest --watchAll", }, "version": "0.0.1", }, @@ -48,7 +47,6 @@ exports[`cached template integration tests first clone with variable replacement exports[`cached template integration tests second clone from cache should snapshot created directory structure from cache 1`] = ` [ - ".boilerplate.json", "LICENSE", "README.md", "__tests__/", @@ -85,7 +83,7 @@ exports[`cached template integration tests second clone from cache should snapsh "scripts": { "lint": "eslint . --fix", "test": "jest", - "test:watch": "jest --watch", + "test:watch": "jest --watchAll", }, "version": "0.0.1", }, diff --git a/packages/create-gen-app-test/src/test-utils/integration-helpers.ts b/packages/create-gen-app-test/src/test-utils/integration-helpers.ts index 23e7acd..3a75512 100644 --- a/packages/create-gen-app-test/src/test-utils/integration-helpers.ts +++ b/packages/create-gen-app-test/src/test-utils/integration-helpers.ts @@ -6,7 +6,7 @@ import { CacheManager, GitCloner } from 'create-gen-app'; export const TEST_REPO = process.env.CREATE_GEN_TEST_REPO ?? - 'https://github.com/constructive-io/pgpm-boilerplates.git'; + 'https://github.com/constructive-io/pgpm-boilerplates-testing.git'; export const TEST_BRANCH = process.env.CREATE_GEN_TEST_BRANCH ?? 'main'; export const TEST_TEMPLATE_DIR = (() => { diff --git a/packages/create-gen-app/README.md b/packages/create-gen-app/README.md index 7d93c37..daa4a91 100644 --- a/packages/create-gen-app/README.md +++ b/packages/create-gen-app/README.md @@ -114,10 +114,11 @@ export const author = "____fullName____"; ### Custom Questions -Create a `.questions.json`: +Create a `.boilerplate.json`: ```json { + "type": "module", "questions": [ { "name": "____fullName____", @@ -135,7 +136,9 @@ Create a `.questions.json`: } ``` -Or `.questions.js` for dynamic logic. Question names can use `____var____` or plain `VAR`; they'll be normalized automatically. +Or `.boilerplate.js` for dynamic logic. Question names can use `____var____` or plain `VAR`; they'll be normalized automatically. + +Note: `.boilerplate.json`, `.boilerplate.js`, and `.boilerplates.json` files are automatically excluded from the generated output. ### License Templates diff --git a/packages/create-gen-app/__tests__/create-gen.test.ts b/packages/create-gen-app/__tests__/create-gen.test.ts index a5ae6ff..6429471 100644 --- a/packages/create-gen-app/__tests__/create-gen.test.ts +++ b/packages/create-gen-app/__tests__/create-gen.test.ts @@ -94,8 +94,9 @@ describe('create-gen-app', () => { ); }); - it('should load questions from .questions.json', async () => { - const questions = { + it('should load questions from .boilerplate.json', async () => { + const boilerplate = { + type: 'module', questions: [ { name: 'projectName', @@ -111,8 +112,8 @@ describe('create-gen-app', () => { }; fs.writeFileSync( - path.join(testTempDir, '.questions.json'), - JSON.stringify(questions, null, 2) + path.join(testTempDir, '.boilerplate.json'), + JSON.stringify(boilerplate, null, 2) ); const result = await extractVariables(testTempDir); @@ -122,9 +123,10 @@ describe('create-gen-app', () => { expect(result.projectQuestions?.questions[0].name).toBe('projectName'); }); - it('should load questions from .questions.js', async () => { - const questionsContent = ` + it('should load questions from .boilerplate.js', async () => { + const boilerplateContent = ` module.exports = { + type: 'module', questions: [ { name: 'projectName', @@ -136,8 +138,8 @@ module.exports = { `; fs.writeFileSync( - path.join(testTempDir, '.questions.js'), - questionsContent + path.join(testTempDir, '.boilerplate.js'), + boilerplateContent ); const result = await extractVariables(testTempDir); @@ -157,9 +159,9 @@ module.exports = { expect(result.projectQuestions).toBeNull(); }); - it('should skip .questions.json and .questions.js from variable extraction', async () => { + it('should skip .boilerplate.json and .boilerplate.js from variable extraction', async () => { fs.writeFileSync( - path.join(testTempDir, '.questions.json'), + path.join(testTempDir, '.boilerplate.json'), '{"questions": [{"name": "____shouldNotExtract____"}]}' ); @@ -486,10 +488,14 @@ module.exports = { expect(content).toBe('export const name = "auth";'); }); - it('should skip .questions.json and .questions.js files', async () => { + it('should skip .boilerplate.json and .boilerplate.js files', async () => { fs.writeFileSync( - path.join(testTempDir, '.questions.json'), - '{"questions": []}' + path.join(testTempDir, '.boilerplate.json'), + '{"type": "module", "questions": []}' + ); + fs.writeFileSync( + path.join(testTempDir, '.boilerplates.json'), + '{"dir": "default"}' ); fs.writeFileSync(path.join(testTempDir, 'README.md'), 'Regular file'); @@ -501,7 +507,10 @@ module.exports = { {} ); - expect(fs.existsSync(path.join(testOutputDir, '.questions.json'))).toBe( + expect(fs.existsSync(path.join(testOutputDir, '.boilerplate.json'))).toBe( + false + ); + expect(fs.existsSync(path.join(testOutputDir, '.boilerplates.json'))).toBe( false ); expect(fs.existsSync(path.join(testOutputDir, 'README.md'))).toBe(true); diff --git a/packages/create-gen-app/src/template/extract.ts b/packages/create-gen-app/src/template/extract.ts index 7f968b0..dbc13b7 100644 --- a/packages/create-gen-app/src/template/extract.ts +++ b/packages/create-gen-app/src/template/extract.ts @@ -35,9 +35,11 @@ export async function extractVariables( await walkDirectory(templateDir, async (filePath) => { const relativePath = path.relative(templateDir, filePath); + // Skip boilerplate configuration files from variable extraction if ( - relativePath === '.questions.json' || - relativePath === '.questions.js' + relativePath === '.boilerplate.json' || + relativePath === '.boilerplate.js' || + relativePath === '.boilerplates.json' ) { return; } @@ -144,36 +146,40 @@ async function walkDirectory( } /** - * Load project questions from .questions.json or .questions.js + * Load project questions from .boilerplate.json or .boilerplate.js * @param templateDir - Path to the template directory * @returns Questions object or null if not found */ async function loadProjectQuestions( templateDir: string ): Promise { - const jsonPath = path.join(templateDir, '.questions.json'); + const jsonPath = path.join(templateDir, '.boilerplate.json'); if (fs.existsSync(jsonPath)) { try { const content = fs.readFileSync(jsonPath, 'utf8'); - const questions = JSON.parse(content); + const parsed = JSON.parse(content); + // .boilerplate.json has { questions: [...] } structure + const questions = parsed.questions ? { questions: parsed.questions } : parsed; return validateQuestions(questions) ? normalizeQuestions(questions) : null; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); - console.warn(`Failed to parse .questions.json: ${errorMessage}`); + console.warn(`Failed to parse .boilerplate.json: ${errorMessage}`); } } - const jsPath = path.join(templateDir, '.questions.js'); + const jsPath = path.join(templateDir, '.boilerplate.js'); if (fs.existsSync(jsPath)) { try { const module = require(jsPath); - const questions = module.default || module; + const exported = module.default || module; + // .boilerplate.js can export { questions: [...] } or just the questions array + const questions = exported.questions ? { questions: exported.questions } : exported; return validateQuestions(questions) ? normalizeQuestions(questions) : null; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); - console.warn(`Failed to load .questions.js: ${errorMessage}`); + console.warn(`Failed to load .boilerplate.js: ${errorMessage}`); } } diff --git a/packages/create-gen-app/src/template/replace.ts b/packages/create-gen-app/src/template/replace.ts index 5597089..57f6cc5 100644 --- a/packages/create-gen-app/src/template/replace.ts +++ b/packages/create-gen-app/src/template/replace.ts @@ -56,7 +56,12 @@ async function walkAndReplace( for (const entry of entries) { const sourceEntryPath = path.join(currentSource, entry.name); - if (entry.name === '.questions.json' || entry.name === '.questions.js') { + // Skip template configuration files - they should not be copied to output + if ( + entry.name === '.boilerplate.json' || + entry.name === '.boilerplate.js' || + entry.name === '.boilerplates.json' + ) { continue; } diff --git a/packages/create-gen-app/src/types.ts b/packages/create-gen-app/src/types.ts index 845b955..5523812 100644 --- a/packages/create-gen-app/src/types.ts +++ b/packages/create-gen-app/src/types.ts @@ -1,7 +1,7 @@ import { Question } from 'inquirerer'; /** - * Questions configuration that can be loaded from .questions.json or .questions.js + * Questions configuration that can be loaded from .boilerplate.json or .boilerplate.js * @typedef {Object} Questions * @property {Question[]} questions - Array of inquirerer questions */