create-gen-app 0.2.2 → 0.3.1

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/esm/licenses.js CHANGED
@@ -2,6 +2,18 @@ import * as fs from "fs";
2
2
  import * as path from "path";
3
3
  const PLACEHOLDER_PATTERN = /{{(\w+)}}/g;
4
4
  let cachedTemplates = null;
5
+ export const LICENSE_VALUE_KEYS = ["LICENSE", "license"];
6
+ export const LICENSE_AUTHOR_KEYS = [
7
+ "USERFULLNAME",
8
+ "AUTHOR",
9
+ "AUTHORFULLNAME",
10
+ "USERNAME",
11
+ "fullName",
12
+ "author",
13
+ "authorFullName",
14
+ "userName",
15
+ ];
16
+ export const LICENSE_EMAIL_KEYS = ["USEREMAIL", "EMAIL", "email", "userEmail"];
5
17
  export function isSupportedLicense(name) {
6
18
  if (!name) {
7
19
  return false;
@@ -36,6 +48,15 @@ export function renderLicense(licenseName, context) {
36
48
  export function listSupportedLicenses() {
37
49
  return Object.keys(loadLicenseTemplates());
38
50
  }
51
+ export function findLicenseValue(answers) {
52
+ return getAnswerValue(answers, LICENSE_VALUE_KEYS);
53
+ }
54
+ export function findLicenseAuthor(answers) {
55
+ return getAnswerValue(answers, LICENSE_AUTHOR_KEYS);
56
+ }
57
+ export function findLicenseEmail(answers) {
58
+ return getAnswerValue(answers, LICENSE_EMAIL_KEYS);
59
+ }
39
60
  function loadLicenseTemplates() {
40
61
  if (cachedTemplates) {
41
62
  return cachedTemplates;
@@ -74,3 +95,12 @@ function findTemplatesDir() {
74
95
  }
75
96
  return null;
76
97
  }
98
+ function getAnswerValue(answers, keys) {
99
+ for (const key of keys) {
100
+ const value = answers?.[key];
101
+ if (typeof value === "string" && value.trim() !== "") {
102
+ return value;
103
+ }
104
+ }
105
+ return undefined;
106
+ }
@@ -16,13 +16,8 @@ export async function extractVariables(templateDir) {
16
16
  const fileReplacerVars = new Set();
17
17
  const contentReplacerVars = new Set();
18
18
  const projectQuestions = await loadProjectQuestions(templateDir);
19
- const ignoredPathPatterns = new Set(projectQuestions?.ignore ?? []);
20
- const ignoredContentTokens = buildIgnoredContentTokens(projectQuestions);
21
19
  await walkDirectory(templateDir, async (filePath) => {
22
20
  const relativePath = path.relative(templateDir, filePath);
23
- if (shouldIgnore(relativePath, ignoredPathPatterns)) {
24
- return;
25
- }
26
21
  if (relativePath === ".questions.json" ||
27
22
  relativePath === ".questions.js") {
28
23
  return;
@@ -38,7 +33,7 @@ export async function extractVariables(templateDir) {
38
33
  });
39
34
  }
40
35
  }
41
- const contentVars = await extractFromFileContent(filePath, ignoredContentTokens);
36
+ const contentVars = await extractFromFileContent(filePath);
42
37
  for (const varName of contentVars) {
43
38
  if (!contentReplacerVars.has(varName)) {
44
39
  contentReplacerVars.add(varName);
@@ -60,7 +55,7 @@ export async function extractVariables(templateDir) {
60
55
  * @param filePath - Path to the file
61
56
  * @returns Set of variable names found in the file
62
57
  */
63
- async function extractFromFileContent(filePath, ignoredTokens) {
58
+ async function extractFromFileContent(filePath) {
64
59
  const variables = new Set();
65
60
  return new Promise((resolve) => {
66
61
  const stream = fs.createReadStream(filePath, { encoding: "utf8" });
@@ -72,18 +67,14 @@ async function extractFromFileContent(filePath, ignoredTokens) {
72
67
  for (const line of lines) {
73
68
  const matches = line.matchAll(VARIABLE_PATTERN);
74
69
  for (const match of matches) {
75
- if (!shouldIgnoreContent(match[0], ignoredTokens)) {
76
- variables.add(match[1]);
77
- }
70
+ variables.add(match[1]);
78
71
  }
79
72
  }
80
73
  });
81
74
  stream.on("end", () => {
82
75
  const matches = buffer.matchAll(VARIABLE_PATTERN);
83
76
  for (const match of matches) {
84
- if (!shouldIgnoreContent(match[0], ignoredTokens)) {
85
- variables.add(match[1]);
86
- }
77
+ variables.add(match[1]);
87
78
  }
88
79
  resolve(variables);
89
80
  });
@@ -141,73 +132,6 @@ async function loadProjectQuestions(templateDir) {
141
132
  }
142
133
  return null;
143
134
  }
144
- function shouldIgnore(relativePath, ignoredPatterns) {
145
- if (ignoredPatterns.size === 0) {
146
- return false;
147
- }
148
- const normalized = relativePath.split(path.sep).join("/");
149
- const segments = normalized.split("/");
150
- for (const pattern of ignoredPatterns) {
151
- const normalizedPattern = pattern.split(path.sep).join("/");
152
- if (normalizedPattern === normalized) {
153
- return true;
154
- }
155
- if (!normalizedPattern.includes("/")) {
156
- if (segments.includes(normalizedPattern)) {
157
- return true;
158
- }
159
- continue;
160
- }
161
- if (normalizedPattern.startsWith("**/")) {
162
- const suffix = normalizedPattern.slice(3);
163
- if (normalized.endsWith(suffix)) {
164
- return true;
165
- }
166
- continue;
167
- }
168
- if (normalizedPattern.endsWith("/**")) {
169
- const prefix = normalizedPattern.slice(0, -3);
170
- if (normalized === prefix || normalized.startsWith(`${prefix}/`)) {
171
- return true;
172
- }
173
- continue;
174
- }
175
- if (normalized.startsWith(`${normalizedPattern}/`)) {
176
- return true;
177
- }
178
- }
179
- return false;
180
- }
181
- const DEFAULT_IGNORED_CONTENT_TOKENS = ["tests", "snapshots"];
182
- function shouldIgnoreContent(match, ignoredTokens) {
183
- if (ignoredTokens.size === 0) {
184
- return false;
185
- }
186
- const token = match.slice(PLACEHOLDER_BOUNDARY.length, -PLACEHOLDER_BOUNDARY.length);
187
- return ignoredTokens.has(token);
188
- }
189
- function buildIgnoredContentTokens(projectQuestions) {
190
- const tokens = new Set(DEFAULT_IGNORED_CONTENT_TOKENS);
191
- if (projectQuestions?.ignore) {
192
- for (const entry of projectQuestions.ignore) {
193
- const normalized = normalizePlaceholder(entry);
194
- if (normalized) {
195
- tokens.add(normalized);
196
- }
197
- }
198
- }
199
- return tokens;
200
- }
201
- function normalizePlaceholder(entry) {
202
- if (entry.startsWith(PLACEHOLDER_BOUNDARY) &&
203
- entry.endsWith(PLACEHOLDER_BOUNDARY)) {
204
- return entry.slice(PLACEHOLDER_BOUNDARY.length, -PLACEHOLDER_BOUNDARY.length);
205
- }
206
- if (entry.startsWith("__") && entry.endsWith("__")) {
207
- return entry.slice(2, -2);
208
- }
209
- return entry || null;
210
- }
211
135
  /**
212
136
  * Normalize questions by ensuring all required fields have default values
213
137
  * @param questions - Questions object to normalize
@@ -68,11 +68,10 @@ export async function promptUser(extractedVariables, argv = {}, noTty = false) {
68
68
  });
69
69
  try {
70
70
  const promptAnswers = await prompter.prompt(preparedArgv, questions);
71
- const mergedAnswers = {
71
+ return {
72
72
  ...argv,
73
73
  ...promptAnswers,
74
74
  };
75
- return expandAnswersForVariables(mergedAnswers, extractedVariables);
76
75
  }
77
76
  finally {
78
77
  if (typeof prompter.close === "function") {
@@ -91,93 +90,10 @@ function mapArgvToQuestions(argv, questions) {
91
90
  if (prepared[name] !== undefined) {
92
91
  continue;
93
92
  }
94
- const matchValue = findMatchingArgValue(name, argvEntries);
95
- if (matchValue !== null) {
96
- prepared[name] = matchValue;
97
- }
98
- }
99
- return prepared;
100
- }
101
- function findMatchingArgValue(targetName, argvEntries) {
102
- const normalizedTarget = normalizeIdentifier(targetName);
103
- for (const [key, value] of argvEntries) {
104
- if (value === undefined || value === null) {
105
- continue;
106
- }
107
- if (key === targetName) {
108
- return value;
109
- }
110
- const normalizedKey = normalizeIdentifier(key);
111
- if (identifiersMatch(normalizedTarget, normalizedKey)) {
112
- return value;
113
- }
114
- }
115
- return null;
116
- }
117
- function normalizeIdentifier(value) {
118
- return value.replace(/[^a-z0-9]/gi, "").toLowerCase();
119
- }
120
- function identifiersMatch(a, b) {
121
- if (a === b) {
122
- return true;
123
- }
124
- if (a.includes(b) || b.includes(a)) {
125
- return true;
126
- }
127
- return hasSignificantOverlap(a, b);
128
- }
129
- function hasSignificantOverlap(a, b) {
130
- const minLength = 4;
131
- if (a.length < minLength || b.length < minLength) {
132
- return false;
133
- }
134
- const shorter = a.length <= b.length ? a : b;
135
- const longer = shorter === a ? b : a;
136
- for (let i = 0; i <= shorter.length - minLength; i++) {
137
- const slice = shorter.slice(i, i + minLength);
138
- if (longer.includes(slice)) {
139
- return true;
140
- }
141
- }
142
- return false;
143
- }
144
- function expandAnswersForVariables(answers, extractedVariables) {
145
- const expanded = { ...answers };
146
- const variables = collectVariableNames(extractedVariables);
147
- const answerEntries = Object.entries(expanded).map(([key, value]) => ({
148
- key,
149
- value,
150
- normalized: normalizeIdentifier(key),
151
- }));
152
- for (const variable of variables) {
153
- if (expanded[variable] !== undefined) {
154
- continue;
155
- }
156
- const normalizedVar = normalizeIdentifier(variable);
157
- const match = answerEntries.find(({ normalized }) => identifiersMatch(normalizedVar, normalized));
93
+ const match = argvEntries.find(([key]) => key === name);
158
94
  if (match) {
159
- expanded[variable] = match.value;
160
- answerEntries.push({
161
- key: variable,
162
- value: match.value,
163
- normalized: normalizedVar,
164
- });
95
+ prepared[name] = match[1];
165
96
  }
166
97
  }
167
- return expanded;
168
- }
169
- function collectVariableNames(extractedVariables) {
170
- const names = new Set();
171
- for (const replacer of extractedVariables.fileReplacers) {
172
- names.add(replacer.variable);
173
- }
174
- for (const replacer of extractedVariables.contentReplacers) {
175
- names.add(replacer.variable);
176
- }
177
- if (extractedVariables.projectQuestions) {
178
- for (const question of extractedVariables.projectQuestions.questions) {
179
- names.add(normalizeQuestionName(question.name));
180
- }
181
- }
182
- return names;
98
+ return prepared;
183
99
  }
@@ -2,7 +2,7 @@ import * as fs from 'fs';
2
2
  import * as path from 'path';
3
3
  import { Transform } from 'stream';
4
4
  import { pipeline } from 'stream/promises';
5
- import { renderLicense, isSupportedLicense } from '../licenses';
5
+ import { renderLicense, isSupportedLicense, findLicenseAuthor, findLicenseEmail, findLicenseValue, } from '../licenses';
6
6
  /**
7
7
  * Replace variables in all files in the template directory
8
8
  * @param templateDir - Path to the template directory
@@ -53,7 +53,7 @@ async function walkAndReplace(sourceDir, destDir, extractedVariables, answers, s
53
53
  }
54
54
  }
55
55
  async function ensureLicenseFile(outputDir, answers) {
56
- const licenseValue = getAnswer(answers, ["LICENSE", "license"]);
56
+ const licenseValue = findLicenseValue(answers);
57
57
  if (typeof licenseValue !== 'string' || licenseValue.trim() === '') {
58
58
  return;
59
59
  }
@@ -62,17 +62,8 @@ async function ensureLicenseFile(outputDir, answers) {
62
62
  console.warn(`[create-gen-app] License "${selectedLicense}" is not supported by the built-in templates. Leaving template LICENSE file as-is.`);
63
63
  return;
64
64
  }
65
- const author = getAnswer(answers, [
66
- "USERFULLNAME",
67
- "AUTHOR",
68
- "AUTHORFULLNAME",
69
- "USERNAME",
70
- "fullName",
71
- "author",
72
- "authorFullName",
73
- "userName",
74
- ]) ?? "Unknown Author";
75
- const email = getAnswer(answers, ["USEREMAIL", "EMAIL", "email", "userEmail"]) ?? "";
65
+ const author = findLicenseAuthor(answers) ?? "Unknown Author";
66
+ const email = findLicenseEmail(answers) ?? "";
76
67
  const content = renderLicense(selectedLicense, {
77
68
  author: String(author),
78
69
  email: String(email || ''),
@@ -85,15 +76,6 @@ async function ensureLicenseFile(outputDir, answers) {
85
76
  fs.writeFileSync(licensePath, content.trimEnd() + '\n', 'utf8');
86
77
  console.log(`[create-gen-app] LICENSE updated with ${selectedLicense} template.`);
87
78
  }
88
- function getAnswer(answers, keys) {
89
- for (const key of keys) {
90
- const value = answers?.[key];
91
- if (typeof value === "string" && value.trim() !== "") {
92
- return value;
93
- }
94
- }
95
- return undefined;
96
- }
97
79
  /**
98
80
  * Replace variables in a file using streams
99
81
  * @param sourcePath - Source file path
package/licenses.d.ts CHANGED
@@ -4,7 +4,13 @@ interface LicenseContext {
4
4
  email: string;
5
5
  }
6
6
  export type SupportedLicense = string;
7
+ export declare const LICENSE_VALUE_KEYS: string[];
8
+ export declare const LICENSE_AUTHOR_KEYS: string[];
9
+ export declare const LICENSE_EMAIL_KEYS: string[];
7
10
  export declare function isSupportedLicense(name: string): name is SupportedLicense;
8
11
  export declare function renderLicense(licenseName: string, context: Partial<LicenseContext>): string | null;
9
12
  export declare function listSupportedLicenses(): string[];
13
+ export declare function findLicenseValue(answers: Record<string, any>): string | undefined;
14
+ export declare function findLicenseAuthor(answers: Record<string, any>): string | undefined;
15
+ export declare function findLicenseEmail(answers: Record<string, any>): string | undefined;
10
16
  export {};
package/licenses.js CHANGED
@@ -33,13 +33,29 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.LICENSE_EMAIL_KEYS = exports.LICENSE_AUTHOR_KEYS = exports.LICENSE_VALUE_KEYS = void 0;
36
37
  exports.isSupportedLicense = isSupportedLicense;
37
38
  exports.renderLicense = renderLicense;
38
39
  exports.listSupportedLicenses = listSupportedLicenses;
40
+ exports.findLicenseValue = findLicenseValue;
41
+ exports.findLicenseAuthor = findLicenseAuthor;
42
+ exports.findLicenseEmail = findLicenseEmail;
39
43
  const fs = __importStar(require("fs"));
40
44
  const path = __importStar(require("path"));
41
45
  const PLACEHOLDER_PATTERN = /{{(\w+)}}/g;
42
46
  let cachedTemplates = null;
47
+ exports.LICENSE_VALUE_KEYS = ["LICENSE", "license"];
48
+ exports.LICENSE_AUTHOR_KEYS = [
49
+ "USERFULLNAME",
50
+ "AUTHOR",
51
+ "AUTHORFULLNAME",
52
+ "USERNAME",
53
+ "fullName",
54
+ "author",
55
+ "authorFullName",
56
+ "userName",
57
+ ];
58
+ exports.LICENSE_EMAIL_KEYS = ["USEREMAIL", "EMAIL", "email", "userEmail"];
43
59
  function isSupportedLicense(name) {
44
60
  if (!name) {
45
61
  return false;
@@ -74,6 +90,15 @@ function renderLicense(licenseName, context) {
74
90
  function listSupportedLicenses() {
75
91
  return Object.keys(loadLicenseTemplates());
76
92
  }
93
+ function findLicenseValue(answers) {
94
+ return getAnswerValue(answers, exports.LICENSE_VALUE_KEYS);
95
+ }
96
+ function findLicenseAuthor(answers) {
97
+ return getAnswerValue(answers, exports.LICENSE_AUTHOR_KEYS);
98
+ }
99
+ function findLicenseEmail(answers) {
100
+ return getAnswerValue(answers, exports.LICENSE_EMAIL_KEYS);
101
+ }
77
102
  function loadLicenseTemplates() {
78
103
  if (cachedTemplates) {
79
104
  return cachedTemplates;
@@ -112,3 +137,12 @@ function findTemplatesDir() {
112
137
  }
113
138
  return null;
114
139
  }
140
+ function getAnswerValue(answers, keys) {
141
+ for (const key of keys) {
142
+ const value = answers?.[key];
143
+ if (typeof value === "string" && value.trim() !== "") {
144
+ return value;
145
+ }
146
+ }
147
+ return undefined;
148
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-gen-app",
3
- "version": "0.2.2",
3
+ "version": "0.3.1",
4
4
  "author": "Dan Lynch <pyramation@gmail.com>",
5
5
  "description": "Clone and customize template repositories with variable replacement",
6
6
  "main": "index.js",
@@ -20,7 +20,7 @@
20
20
  "url": "https://github.com/hyperweb-io/dev-utils/issues"
21
21
  },
22
22
  "scripts": {
23
- "copy": "copyfiles -f ../../LICENSE README.md package.json \"licenses-templates/**/*\" dist",
23
+ "copy": "copyfiles -f ../../LICENSE README.md package.json dist && copyfiles -f \"licenses-templates/**/*\" dist/licenses-templates",
24
24
  "clean": "makage clean",
25
25
  "prepublishOnly": "npm run build",
26
26
  "build": "npm run clean && tsc && tsc -p tsconfig.esm.json && npm run copy",
@@ -36,5 +36,5 @@
36
36
  "makage": "0.1.8"
37
37
  },
38
38
  "keywords": [],
39
- "gitHead": "872fb2ae9f7e3d541e94271c26099bd85d5237d2"
39
+ "gitHead": "3e7186aed1ddfe77a1c03d4e1ca48d7a1811b53f"
40
40
  }
@@ -52,13 +52,8 @@ async function extractVariables(templateDir) {
52
52
  const fileReplacerVars = new Set();
53
53
  const contentReplacerVars = new Set();
54
54
  const projectQuestions = await loadProjectQuestions(templateDir);
55
- const ignoredPathPatterns = new Set(projectQuestions?.ignore ?? []);
56
- const ignoredContentTokens = buildIgnoredContentTokens(projectQuestions);
57
55
  await walkDirectory(templateDir, async (filePath) => {
58
56
  const relativePath = path.relative(templateDir, filePath);
59
- if (shouldIgnore(relativePath, ignoredPathPatterns)) {
60
- return;
61
- }
62
57
  if (relativePath === ".questions.json" ||
63
58
  relativePath === ".questions.js") {
64
59
  return;
@@ -74,7 +69,7 @@ async function extractVariables(templateDir) {
74
69
  });
75
70
  }
76
71
  }
77
- const contentVars = await extractFromFileContent(filePath, ignoredContentTokens);
72
+ const contentVars = await extractFromFileContent(filePath);
78
73
  for (const varName of contentVars) {
79
74
  if (!contentReplacerVars.has(varName)) {
80
75
  contentReplacerVars.add(varName);
@@ -96,7 +91,7 @@ async function extractVariables(templateDir) {
96
91
  * @param filePath - Path to the file
97
92
  * @returns Set of variable names found in the file
98
93
  */
99
- async function extractFromFileContent(filePath, ignoredTokens) {
94
+ async function extractFromFileContent(filePath) {
100
95
  const variables = new Set();
101
96
  return new Promise((resolve) => {
102
97
  const stream = fs.createReadStream(filePath, { encoding: "utf8" });
@@ -108,18 +103,14 @@ async function extractFromFileContent(filePath, ignoredTokens) {
108
103
  for (const line of lines) {
109
104
  const matches = line.matchAll(VARIABLE_PATTERN);
110
105
  for (const match of matches) {
111
- if (!shouldIgnoreContent(match[0], ignoredTokens)) {
112
- variables.add(match[1]);
113
- }
106
+ variables.add(match[1]);
114
107
  }
115
108
  }
116
109
  });
117
110
  stream.on("end", () => {
118
111
  const matches = buffer.matchAll(VARIABLE_PATTERN);
119
112
  for (const match of matches) {
120
- if (!shouldIgnoreContent(match[0], ignoredTokens)) {
121
- variables.add(match[1]);
122
- }
113
+ variables.add(match[1]);
123
114
  }
124
115
  resolve(variables);
125
116
  });
@@ -177,73 +168,6 @@ async function loadProjectQuestions(templateDir) {
177
168
  }
178
169
  return null;
179
170
  }
180
- function shouldIgnore(relativePath, ignoredPatterns) {
181
- if (ignoredPatterns.size === 0) {
182
- return false;
183
- }
184
- const normalized = relativePath.split(path.sep).join("/");
185
- const segments = normalized.split("/");
186
- for (const pattern of ignoredPatterns) {
187
- const normalizedPattern = pattern.split(path.sep).join("/");
188
- if (normalizedPattern === normalized) {
189
- return true;
190
- }
191
- if (!normalizedPattern.includes("/")) {
192
- if (segments.includes(normalizedPattern)) {
193
- return true;
194
- }
195
- continue;
196
- }
197
- if (normalizedPattern.startsWith("**/")) {
198
- const suffix = normalizedPattern.slice(3);
199
- if (normalized.endsWith(suffix)) {
200
- return true;
201
- }
202
- continue;
203
- }
204
- if (normalizedPattern.endsWith("/**")) {
205
- const prefix = normalizedPattern.slice(0, -3);
206
- if (normalized === prefix || normalized.startsWith(`${prefix}/`)) {
207
- return true;
208
- }
209
- continue;
210
- }
211
- if (normalized.startsWith(`${normalizedPattern}/`)) {
212
- return true;
213
- }
214
- }
215
- return false;
216
- }
217
- const DEFAULT_IGNORED_CONTENT_TOKENS = ["tests", "snapshots"];
218
- function shouldIgnoreContent(match, ignoredTokens) {
219
- if (ignoredTokens.size === 0) {
220
- return false;
221
- }
222
- const token = match.slice(PLACEHOLDER_BOUNDARY.length, -PLACEHOLDER_BOUNDARY.length);
223
- return ignoredTokens.has(token);
224
- }
225
- function buildIgnoredContentTokens(projectQuestions) {
226
- const tokens = new Set(DEFAULT_IGNORED_CONTENT_TOKENS);
227
- if (projectQuestions?.ignore) {
228
- for (const entry of projectQuestions.ignore) {
229
- const normalized = normalizePlaceholder(entry);
230
- if (normalized) {
231
- tokens.add(normalized);
232
- }
233
- }
234
- }
235
- return tokens;
236
- }
237
- function normalizePlaceholder(entry) {
238
- if (entry.startsWith(PLACEHOLDER_BOUNDARY) &&
239
- entry.endsWith(PLACEHOLDER_BOUNDARY)) {
240
- return entry.slice(PLACEHOLDER_BOUNDARY.length, -PLACEHOLDER_BOUNDARY.length);
241
- }
242
- if (entry.startsWith("__") && entry.endsWith("__")) {
243
- return entry.slice(2, -2);
244
- }
245
- return entry || null;
246
- }
247
171
  /**
248
172
  * Normalize questions by ensuring all required fields have default values
249
173
  * @param questions - Questions object to normalize
@@ -72,11 +72,10 @@ async function promptUser(extractedVariables, argv = {}, noTty = false) {
72
72
  });
73
73
  try {
74
74
  const promptAnswers = await prompter.prompt(preparedArgv, questions);
75
- const mergedAnswers = {
75
+ return {
76
76
  ...argv,
77
77
  ...promptAnswers,
78
78
  };
79
- return expandAnswersForVariables(mergedAnswers, extractedVariables);
80
79
  }
81
80
  finally {
82
81
  if (typeof prompter.close === "function") {
@@ -95,93 +94,10 @@ function mapArgvToQuestions(argv, questions) {
95
94
  if (prepared[name] !== undefined) {
96
95
  continue;
97
96
  }
98
- const matchValue = findMatchingArgValue(name, argvEntries);
99
- if (matchValue !== null) {
100
- prepared[name] = matchValue;
101
- }
102
- }
103
- return prepared;
104
- }
105
- function findMatchingArgValue(targetName, argvEntries) {
106
- const normalizedTarget = normalizeIdentifier(targetName);
107
- for (const [key, value] of argvEntries) {
108
- if (value === undefined || value === null) {
109
- continue;
110
- }
111
- if (key === targetName) {
112
- return value;
113
- }
114
- const normalizedKey = normalizeIdentifier(key);
115
- if (identifiersMatch(normalizedTarget, normalizedKey)) {
116
- return value;
117
- }
118
- }
119
- return null;
120
- }
121
- function normalizeIdentifier(value) {
122
- return value.replace(/[^a-z0-9]/gi, "").toLowerCase();
123
- }
124
- function identifiersMatch(a, b) {
125
- if (a === b) {
126
- return true;
127
- }
128
- if (a.includes(b) || b.includes(a)) {
129
- return true;
130
- }
131
- return hasSignificantOverlap(a, b);
132
- }
133
- function hasSignificantOverlap(a, b) {
134
- const minLength = 4;
135
- if (a.length < minLength || b.length < minLength) {
136
- return false;
137
- }
138
- const shorter = a.length <= b.length ? a : b;
139
- const longer = shorter === a ? b : a;
140
- for (let i = 0; i <= shorter.length - minLength; i++) {
141
- const slice = shorter.slice(i, i + minLength);
142
- if (longer.includes(slice)) {
143
- return true;
144
- }
145
- }
146
- return false;
147
- }
148
- function expandAnswersForVariables(answers, extractedVariables) {
149
- const expanded = { ...answers };
150
- const variables = collectVariableNames(extractedVariables);
151
- const answerEntries = Object.entries(expanded).map(([key, value]) => ({
152
- key,
153
- value,
154
- normalized: normalizeIdentifier(key),
155
- }));
156
- for (const variable of variables) {
157
- if (expanded[variable] !== undefined) {
158
- continue;
159
- }
160
- const normalizedVar = normalizeIdentifier(variable);
161
- const match = answerEntries.find(({ normalized }) => identifiersMatch(normalizedVar, normalized));
97
+ const match = argvEntries.find(([key]) => key === name);
162
98
  if (match) {
163
- expanded[variable] = match.value;
164
- answerEntries.push({
165
- key: variable,
166
- value: match.value,
167
- normalized: normalizedVar,
168
- });
99
+ prepared[name] = match[1];
169
100
  }
170
101
  }
171
- return expanded;
172
- }
173
- function collectVariableNames(extractedVariables) {
174
- const names = new Set();
175
- for (const replacer of extractedVariables.fileReplacers) {
176
- names.add(replacer.variable);
177
- }
178
- for (const replacer of extractedVariables.contentReplacers) {
179
- names.add(replacer.variable);
180
- }
181
- if (extractedVariables.projectQuestions) {
182
- for (const question of extractedVariables.projectQuestions.questions) {
183
- names.add(normalizeQuestionName(question.name));
184
- }
185
- }
186
- return names;
102
+ return prepared;
187
103
  }
@@ -89,7 +89,7 @@ async function walkAndReplace(sourceDir, destDir, extractedVariables, answers, s
89
89
  }
90
90
  }
91
91
  async function ensureLicenseFile(outputDir, answers) {
92
- const licenseValue = getAnswer(answers, ["LICENSE", "license"]);
92
+ const licenseValue = (0, licenses_1.findLicenseValue)(answers);
93
93
  if (typeof licenseValue !== 'string' || licenseValue.trim() === '') {
94
94
  return;
95
95
  }
@@ -98,17 +98,8 @@ async function ensureLicenseFile(outputDir, answers) {
98
98
  console.warn(`[create-gen-app] License "${selectedLicense}" is not supported by the built-in templates. Leaving template LICENSE file as-is.`);
99
99
  return;
100
100
  }
101
- const author = getAnswer(answers, [
102
- "USERFULLNAME",
103
- "AUTHOR",
104
- "AUTHORFULLNAME",
105
- "USERNAME",
106
- "fullName",
107
- "author",
108
- "authorFullName",
109
- "userName",
110
- ]) ?? "Unknown Author";
111
- const email = getAnswer(answers, ["USEREMAIL", "EMAIL", "email", "userEmail"]) ?? "";
101
+ const author = (0, licenses_1.findLicenseAuthor)(answers) ?? "Unknown Author";
102
+ const email = (0, licenses_1.findLicenseEmail)(answers) ?? "";
112
103
  const content = (0, licenses_1.renderLicense)(selectedLicense, {
113
104
  author: String(author),
114
105
  email: String(email || ''),
@@ -121,15 +112,6 @@ async function ensureLicenseFile(outputDir, answers) {
121
112
  fs.writeFileSync(licensePath, content.trimEnd() + '\n', 'utf8');
122
113
  console.log(`[create-gen-app] LICENSE updated with ${selectedLicense} template.`);
123
114
  }
124
- function getAnswer(answers, keys) {
125
- for (const key of keys) {
126
- const value = answers?.[key];
127
- if (typeof value === "string" && value.trim() !== "") {
128
- return value;
129
- }
130
- }
131
- return undefined;
132
- }
133
115
  /**
134
116
  * Replace variables in a file using streams
135
117
  * @param sourcePath - Source file path
package/types.d.ts CHANGED
@@ -6,7 +6,6 @@ import { Question } from 'inquirerer';
6
6
  */
7
7
  export interface Questions {
8
8
  questions: Question[];
9
- ignore?: string[];
10
9
  }
11
10
  /**
12
11
  * Variable extracted from filename patterns like ____variable____
File without changes
File without changes
File without changes
File without changes