@nitrostack/cli 1.0.14 → 1.0.15
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/assets/canonical.gitignore +57 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +8 -7
- package/dist/commands/pack.d.ts +10 -0
- package/dist/commands/pack.d.ts.map +1 -0
- package/dist/commands/pack.js +82 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -0
- package/dist/pack/canonical-gitignore.d.ts +14 -0
- package/dist/pack/canonical-gitignore.d.ts.map +1 -0
- package/dist/pack/canonical-gitignore.js +28 -0
- package/dist/pack/exclusions.d.ts +8 -0
- package/dist/pack/exclusions.d.ts.map +1 -0
- package/dist/pack/exclusions.js +84 -0
- package/dist/pack/gitignore.d.ts +21 -0
- package/dist/pack/gitignore.d.ts.map +1 -0
- package/dist/pack/gitignore.js +123 -0
- package/dist/pack/ignore-matcher.d.ts +19 -0
- package/dist/pack/ignore-matcher.d.ts.map +1 -0
- package/dist/pack/ignore-matcher.js +149 -0
- package/dist/pack/index.d.ts +11 -0
- package/dist/pack/index.d.ts.map +1 -0
- package/dist/pack/index.js +8 -0
- package/dist/pack/pack-project.d.ts +6 -0
- package/dist/pack/pack-project.d.ts.map +1 -0
- package/dist/pack/pack-project.js +59 -0
- package/dist/pack/standalone.d.ts +3 -0
- package/dist/pack/standalone.d.ts.map +1 -0
- package/dist/pack/standalone.js +95 -0
- package/dist/pack/tree.d.ts +5 -0
- package/dist/pack/tree.d.ts.map +1 -0
- package/dist/pack/tree.js +70 -0
- package/dist/pack/types.d.ts +35 -0
- package/dist/pack/types.d.ts.map +1 -0
- package/dist/pack/types.js +1 -0
- package/dist/pack/validate-project.d.ts +9 -0
- package/dist/pack/validate-project.d.ts.map +1 -0
- package/dist/pack/validate-project.js +44 -0
- package/dist/pack/zipper.d.ts +20 -0
- package/dist/pack/zipper.d.ts.map +1 -0
- package/dist/pack/zipper.js +121 -0
- package/package.json +6 -3
- package/templates/typescript-oauth/.env.example +1 -1
- package/templates/typescript-oauth/_gitignore +57 -0
- package/templates/typescript-pizzaz/.env.example +1 -1
- package/templates/typescript-pizzaz/_gitignore +57 -0
- package/templates/typescript-starter/.env.example +1 -1
- package/templates/typescript-starter/_gitignore +57 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Dependencies
|
|
2
|
+
node_modules/
|
|
3
|
+
src/widgets/node_modules/
|
|
4
|
+
|
|
5
|
+
# Build outputs
|
|
6
|
+
dist/
|
|
7
|
+
src/widgets/.next/
|
|
8
|
+
src/widgets/out/
|
|
9
|
+
|
|
10
|
+
# Environment files
|
|
11
|
+
.env
|
|
12
|
+
.env.local
|
|
13
|
+
.env.*.local
|
|
14
|
+
|
|
15
|
+
# IDE
|
|
16
|
+
.idea/
|
|
17
|
+
.vscode/
|
|
18
|
+
*.swp
|
|
19
|
+
*.swo
|
|
20
|
+
*~
|
|
21
|
+
|
|
22
|
+
# OS files
|
|
23
|
+
.DS_Store
|
|
24
|
+
Thumbs.db
|
|
25
|
+
|
|
26
|
+
# Logs
|
|
27
|
+
*.log
|
|
28
|
+
npm-debug.log*
|
|
29
|
+
yarn-debug.log*
|
|
30
|
+
yarn-error.log*
|
|
31
|
+
|
|
32
|
+
# Runtime data
|
|
33
|
+
pids/
|
|
34
|
+
*.pid
|
|
35
|
+
*.seed
|
|
36
|
+
*.pid.lock
|
|
37
|
+
|
|
38
|
+
# Coverage
|
|
39
|
+
coverage/
|
|
40
|
+
.nyc_output/
|
|
41
|
+
|
|
42
|
+
# Uploads
|
|
43
|
+
uploads/
|
|
44
|
+
|
|
45
|
+
# TypeScript cache
|
|
46
|
+
*.tsbuildinfo
|
|
47
|
+
|
|
48
|
+
# Optional npm cache
|
|
49
|
+
.npm/
|
|
50
|
+
|
|
51
|
+
# Optional eslint cache
|
|
52
|
+
.eslintcache
|
|
53
|
+
|
|
54
|
+
# OAuth tokens/secrets (never commit these!)
|
|
55
|
+
*.pem
|
|
56
|
+
*.key
|
|
57
|
+
tokens.json
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AA6IA,UAAU,WAAW;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,wBAAsB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,WAAW,iBA2PtF"}
|
package/dist/commands/init.js
CHANGED
|
@@ -8,6 +8,7 @@ import { execSync } from 'child_process';
|
|
|
8
8
|
import { NITRO_BANNER_FULL, createSuccessBox, NitroSpinner, log, spacer, nextSteps, brand, showFooter } from '../ui/branding.js';
|
|
9
9
|
import { trackEvent, shutdownAnalytics } from '../analytics/posthog.js';
|
|
10
10
|
import { runSkillsFlow } from '../skills/index.js';
|
|
11
|
+
import { writeCanonicalGitignore } from '../pack/canonical-gitignore.js';
|
|
11
12
|
import readline from 'readline';
|
|
12
13
|
/**
|
|
13
14
|
* Prompts the user with a horizontal YES/NO choice using left/right arrow keys.
|
|
@@ -201,6 +202,11 @@ export async function initCommand(projectName, options) {
|
|
|
201
202
|
// Copy template files
|
|
202
203
|
if (fs.existsSync(templateDir)) {
|
|
203
204
|
fs.copySync(templateDir, targetDir);
|
|
205
|
+
// Template ships `_gitignore` (npm never packs files named `.gitignore`)
|
|
206
|
+
const placeholder = path.join(targetDir, '_gitignore');
|
|
207
|
+
if (fs.existsSync(placeholder)) {
|
|
208
|
+
fs.removeSync(placeholder);
|
|
209
|
+
}
|
|
204
210
|
}
|
|
205
211
|
else {
|
|
206
212
|
await createProjectFromScratch(targetDir, finalProjectName, {
|
|
@@ -209,6 +215,8 @@ export async function initCommand(projectName, options) {
|
|
|
209
215
|
author: finalAuthor,
|
|
210
216
|
});
|
|
211
217
|
}
|
|
218
|
+
// Always write canonical .gitignore — npm strips `.gitignore` from published templates
|
|
219
|
+
await writeCanonicalGitignore(targetDir);
|
|
212
220
|
// Update package.json
|
|
213
221
|
const packageJsonPath = path.join(targetDir, 'package.json');
|
|
214
222
|
if (fs.existsSync(packageJsonPath)) {
|
|
@@ -395,13 +403,6 @@ server.start().catch((error) => {
|
|
|
395
403
|
`;
|
|
396
404
|
fs.writeFileSync(path.join(targetDir, 'src', 'index.ts'), indexTs);
|
|
397
405
|
fs.writeFileSync(path.join(targetDir, '.env'), '# Environment variables\n');
|
|
398
|
-
const gitignore = `node_modules/
|
|
399
|
-
dist/
|
|
400
|
-
.env
|
|
401
|
-
.env.local
|
|
402
|
-
*.log
|
|
403
|
-
`;
|
|
404
|
-
fs.writeFileSync(path.join(targetDir, '.gitignore'), gitignore);
|
|
405
406
|
const readme = `# ${projectName}
|
|
406
407
|
|
|
407
408
|
${answers.description}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
interface PackCommandOptions {
|
|
2
|
+
output?: string;
|
|
3
|
+
dryRun?: boolean;
|
|
4
|
+
syncGitignore?: boolean;
|
|
5
|
+
includeEnv?: boolean;
|
|
6
|
+
cwd?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function packCommand(options: PackCommandOptions): Promise<void>;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=pack.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pack.d.ts","sourceRoot":"","sources":["../../src/commands/pack.ts"],"names":[],"mappings":"AAiBA,UAAU,kBAAkB;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AA2CD,wBAAsB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CA2D5E"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { createHeader, createSuccessBox, createErrorBox, NitroSpinner, spacer, brand, NITRO_BANNER_FULL, showFooter, } from '../ui/branding.js';
|
|
4
|
+
import { trackEvent, shutdownAnalytics } from '../analytics/posthog.js';
|
|
5
|
+
import { packProject } from '../pack/pack-project.js';
|
|
6
|
+
import { PackValidationError } from '../pack/validate-project.js';
|
|
7
|
+
function formatBytes(bytes) {
|
|
8
|
+
if (bytes < 1024)
|
|
9
|
+
return `${bytes} B`;
|
|
10
|
+
if (bytes < 1024 * 1024)
|
|
11
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
12
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
13
|
+
}
|
|
14
|
+
function buildSummaryItems(result) {
|
|
15
|
+
const items = [
|
|
16
|
+
`${brand.skyBold(String(result.filesIncluded))} file(s) included`,
|
|
17
|
+
];
|
|
18
|
+
if (result.outputPath) {
|
|
19
|
+
items.push(`Output: ${brand.sky(result.outputPath)}`);
|
|
20
|
+
}
|
|
21
|
+
if (result.zipSizeBytes !== null) {
|
|
22
|
+
items.push(`Size: ${brand.skyBold(formatBytes(result.zipSizeBytes))}`);
|
|
23
|
+
}
|
|
24
|
+
if (result.gitignoreUpdated) {
|
|
25
|
+
items.push(`Added ${result.addedGitignoreRules.length} .gitignore rule(s)`);
|
|
26
|
+
}
|
|
27
|
+
return items;
|
|
28
|
+
}
|
|
29
|
+
function printExcludedCategories(result) {
|
|
30
|
+
console.log(chalk.bold('\nExcluded from zip:'));
|
|
31
|
+
for (const category of result.excludedCategories) {
|
|
32
|
+
console.log(` ${chalk.cyan(category.category)}: ${category.paths.join(', ')}`);
|
|
33
|
+
}
|
|
34
|
+
spacer();
|
|
35
|
+
}
|
|
36
|
+
function printDryRunTree(result) {
|
|
37
|
+
if (!result.dryRunTree)
|
|
38
|
+
return;
|
|
39
|
+
console.log(chalk.bold('File tree:'));
|
|
40
|
+
console.log(result.dryRunTree);
|
|
41
|
+
spacer();
|
|
42
|
+
}
|
|
43
|
+
export async function packCommand(options) {
|
|
44
|
+
console.log(NITRO_BANNER_FULL);
|
|
45
|
+
console.log(createHeader('Pack', 'Create optimized project archive'));
|
|
46
|
+
trackEvent('cli_command_invoked', {
|
|
47
|
+
command: 'pack',
|
|
48
|
+
options: Object.keys(options).filter((key) => options[key] !== undefined),
|
|
49
|
+
});
|
|
50
|
+
const packOptions = {
|
|
51
|
+
output: options.output,
|
|
52
|
+
dryRun: options.dryRun,
|
|
53
|
+
syncGitignore: options.syncGitignore ?? true,
|
|
54
|
+
includeEnv: options.includeEnv ?? false,
|
|
55
|
+
cwd: path.resolve(options.cwd ?? process.cwd()),
|
|
56
|
+
};
|
|
57
|
+
const spinner = new NitroSpinner(packOptions.dryRun ? 'Analyzing project files...' : 'Creating optimized zip...').start();
|
|
58
|
+
try {
|
|
59
|
+
const result = await packProject(packOptions);
|
|
60
|
+
spinner.succeed(packOptions.dryRun
|
|
61
|
+
? 'Dry run complete'
|
|
62
|
+
: `Created ${path.basename(result.outputPath ?? 'archive')}`);
|
|
63
|
+
printExcludedCategories(result);
|
|
64
|
+
if (packOptions.dryRun) {
|
|
65
|
+
printDryRunTree(result);
|
|
66
|
+
}
|
|
67
|
+
console.log(createSuccessBox(packOptions.dryRun ? 'Dry Run Complete' : 'Project Packed', buildSummaryItems(result)));
|
|
68
|
+
showFooter();
|
|
69
|
+
await shutdownAnalytics();
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
spinner.fail('Pack failed');
|
|
73
|
+
if (error instanceof PackValidationError) {
|
|
74
|
+
console.log(createErrorBox('Pack Failed', error.message));
|
|
75
|
+
await shutdownAnalytics();
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
console.log(createErrorBox('Pack Failed', error instanceof Error ? error.message : String(error)));
|
|
79
|
+
await shutdownAnalytics();
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -9,4 +9,6 @@ export { generate } from './commands/generate.js';
|
|
|
9
9
|
export { upgradeCommand } from './commands/upgrade.js';
|
|
10
10
|
export { installCommand } from './commands/install.js';
|
|
11
11
|
export { cursorCommand } from './commands/cursor.js';
|
|
12
|
+
export { packCommand } from './commands/pack.js';
|
|
13
|
+
export { packProject } from './pack/pack-project.js';
|
|
12
14
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAepC,wBAAgB,aAAa,YAuF5B;AAGD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -9,6 +9,7 @@ import { generate } from './commands/generate.js';
|
|
|
9
9
|
import { upgradeCommand } from './commands/upgrade.js';
|
|
10
10
|
import { installCommand } from './commands/install.js';
|
|
11
11
|
import { cursorCommand } from './commands/cursor.js';
|
|
12
|
+
import { packCommand } from './commands/pack.js';
|
|
12
13
|
const require = createRequire(import.meta.url);
|
|
13
14
|
const packageJson = require('../package.json');
|
|
14
15
|
export function createProgram() {
|
|
@@ -77,6 +78,15 @@ export function createProgram() {
|
|
|
77
78
|
.option('-p, --port <port>', 'Port for default HTTP URL (for legacy-sse or streamable-http)')
|
|
78
79
|
.option('--force', 'Force overwrite of existing configuration')
|
|
79
80
|
.action(cursorCommand);
|
|
81
|
+
program
|
|
82
|
+
.command('pack')
|
|
83
|
+
.description('Create an optimized zip of the project (excludes build artifacts)')
|
|
84
|
+
.option('-o, --output <path>', 'Zip filename (always written to the project root)')
|
|
85
|
+
.option('--dry-run', 'Show excluded/included summary without creating zip')
|
|
86
|
+
.option('--include-env', 'Include .env files in the zip')
|
|
87
|
+
.option('--no-sync-gitignore', 'Skip merging canonical rules into local .gitignore')
|
|
88
|
+
.option('--cwd <dir>', 'Project directory', process.cwd())
|
|
89
|
+
.action(packCommand);
|
|
80
90
|
return program;
|
|
81
91
|
}
|
|
82
92
|
// Re-export commands for programmatic use
|
|
@@ -88,6 +98,8 @@ export { generate } from './commands/generate.js';
|
|
|
88
98
|
export { upgradeCommand } from './commands/upgrade.js';
|
|
89
99
|
export { installCommand } from './commands/install.js';
|
|
90
100
|
export { cursorCommand } from './commands/cursor.js';
|
|
101
|
+
export { packCommand } from './commands/pack.js';
|
|
102
|
+
export { packProject } from './pack/pack-project.js';
|
|
91
103
|
// Run the CLI when this module is the entry point
|
|
92
104
|
import { fileURLToPath } from 'url';
|
|
93
105
|
import { realpathSync } from 'fs';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve the bundled canonical .gitignore shipped with the CLI package.
|
|
3
|
+
*/
|
|
4
|
+
export declare function getCanonicalGitignorePath(): string;
|
|
5
|
+
/**
|
|
6
|
+
* Load canonical .gitignore rules from the bundled starter template copy.
|
|
7
|
+
*/
|
|
8
|
+
export declare function loadCanonicalGitignore(): Promise<string>;
|
|
9
|
+
/**
|
|
10
|
+
* Write the bundled canonical .gitignore into a project root as `.gitignore`.
|
|
11
|
+
* Used by init because npm never ships files named `.gitignore` in the package tarball.
|
|
12
|
+
*/
|
|
13
|
+
export declare function writeCanonicalGitignore(projectRoot: string): Promise<void>;
|
|
14
|
+
//# sourceMappingURL=canonical-gitignore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canonical-gitignore.d.ts","sourceRoot":"","sources":["../../src/pack/canonical-gitignore.ts"],"names":[],"mappings":"AAMA;;GAEG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CAElD;AAED;;GAEG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,MAAM,CAAC,CAM9D;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGhF"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { fileURLToPath } from 'url';
|
|
3
|
+
import fs from 'fs-extra';
|
|
4
|
+
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
|
5
|
+
/**
|
|
6
|
+
* Resolve the bundled canonical .gitignore shipped with the CLI package.
|
|
7
|
+
*/
|
|
8
|
+
export function getCanonicalGitignorePath() {
|
|
9
|
+
return path.join(moduleDir, '../../assets/canonical.gitignore');
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Load canonical .gitignore rules from the bundled starter template copy.
|
|
13
|
+
*/
|
|
14
|
+
export async function loadCanonicalGitignore() {
|
|
15
|
+
const canonicalPath = getCanonicalGitignorePath();
|
|
16
|
+
if (!(await fs.pathExists(canonicalPath))) {
|
|
17
|
+
throw new Error(`Canonical .gitignore not found at ${canonicalPath}`);
|
|
18
|
+
}
|
|
19
|
+
return fs.readFile(canonicalPath, 'utf-8');
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Write the bundled canonical .gitignore into a project root as `.gitignore`.
|
|
23
|
+
* Used by init because npm never ships files named `.gitignore` in the package tarball.
|
|
24
|
+
*/
|
|
25
|
+
export async function writeCanonicalGitignore(projectRoot) {
|
|
26
|
+
const content = await loadCanonicalGitignore();
|
|
27
|
+
await fs.writeFile(path.join(projectRoot, '.gitignore'), content, 'utf-8');
|
|
28
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ExclusionCategory } from './types.js';
|
|
2
|
+
/** Env/secret patterns excluded by default; skipped when --include-env is set. */
|
|
3
|
+
export declare const ENV_EXCLUDED_PATTERNS: string[];
|
|
4
|
+
/** Hard-coded exclusions always applied in addition to merged .gitignore rules. */
|
|
5
|
+
export declare const HARD_EXCLUDED_PATTERNS: string[];
|
|
6
|
+
export declare const EXCLUSION_CATEGORIES: ExclusionCategory[];
|
|
7
|
+
export declare function getExclusionReport(includeEnv?: boolean): ExclusionCategory[];
|
|
8
|
+
//# sourceMappingURL=exclusions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exclusions.d.ts","sourceRoot":"","sources":["../../src/pack/exclusions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD,kFAAkF;AAClF,eAAO,MAAM,qBAAqB,EAAE,MAAM,EAIzC,CAAC;AAEF,mFAAmF;AACnF,eAAO,MAAM,sBAAsB,EAAE,MAAM,EA0B1C,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,iBAAiB,EAyCnD,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,UAAU,GAAE,OAAe,GAAG,iBAAiB,EAAE,CAOnF"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/** Env/secret patterns excluded by default; skipped when --include-env is set. */
|
|
2
|
+
export const ENV_EXCLUDED_PATTERNS = [
|
|
3
|
+
'.env',
|
|
4
|
+
'.env.local',
|
|
5
|
+
'.env.*.local',
|
|
6
|
+
];
|
|
7
|
+
/** Hard-coded exclusions always applied in addition to merged .gitignore rules. */
|
|
8
|
+
export const HARD_EXCLUDED_PATTERNS = [
|
|
9
|
+
'.git/',
|
|
10
|
+
'**/node_modules/',
|
|
11
|
+
'**/dist/',
|
|
12
|
+
'**/build/',
|
|
13
|
+
'**/out/',
|
|
14
|
+
'**/.next/',
|
|
15
|
+
'*.tsbuildinfo',
|
|
16
|
+
'.npm/',
|
|
17
|
+
'.eslintcache',
|
|
18
|
+
'coverage/',
|
|
19
|
+
'.nyc_output/',
|
|
20
|
+
'tmp/',
|
|
21
|
+
'temp/',
|
|
22
|
+
'pids/',
|
|
23
|
+
'uploads/',
|
|
24
|
+
...ENV_EXCLUDED_PATTERNS,
|
|
25
|
+
'.idea/',
|
|
26
|
+
'.vscode/',
|
|
27
|
+
'.DS_Store',
|
|
28
|
+
'Thumbs.db',
|
|
29
|
+
'*.log',
|
|
30
|
+
'npm-debug.log*',
|
|
31
|
+
'yarn-debug.log*',
|
|
32
|
+
'yarn-error.log*',
|
|
33
|
+
'*.zip',
|
|
34
|
+
];
|
|
35
|
+
export const EXCLUSION_CATEGORIES = [
|
|
36
|
+
{
|
|
37
|
+
category: 'VCS',
|
|
38
|
+
paths: ['.git/'],
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
category: 'Dependencies',
|
|
42
|
+
paths: ['**/node_modules/'],
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
category: 'Build outputs',
|
|
46
|
+
paths: ['**/dist/', '**/build/', '**/out/', '**/.next/'],
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
category: 'Caches',
|
|
50
|
+
paths: ['*.tsbuildinfo', '.npm/', '.eslintcache'],
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
category: 'Test',
|
|
54
|
+
paths: ['coverage/', '.nyc_output/'],
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
category: 'Temp/runtime',
|
|
58
|
+
paths: ['tmp/', 'temp/', 'pids/', 'uploads/'],
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
category: 'Secrets',
|
|
62
|
+
paths: [...ENV_EXCLUDED_PATTERNS],
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
category: 'IDE/OS',
|
|
66
|
+
paths: ['.idea/', '.vscode/', '.DS_Store', 'Thumbs.db'],
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
category: 'Logs',
|
|
70
|
+
paths: ['*.log', 'npm-debug.log*', 'yarn-debug.log*', 'yarn-error.log*'],
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
category: 'Archives',
|
|
74
|
+
paths: ['*.zip'],
|
|
75
|
+
},
|
|
76
|
+
];
|
|
77
|
+
export function getExclusionReport(includeEnv = false) {
|
|
78
|
+
return EXCLUSION_CATEGORIES
|
|
79
|
+
.filter((entry) => !(includeEnv && entry.category === 'Secrets'))
|
|
80
|
+
.map((entry) => ({
|
|
81
|
+
category: entry.category,
|
|
82
|
+
paths: [...entry.paths],
|
|
83
|
+
}));
|
|
84
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type IgnoreMatcher } from './ignore-matcher.js';
|
|
2
|
+
import type { GitignoreMergeResult } from './types.js';
|
|
3
|
+
export interface BuildIgnoreMatcherOptions {
|
|
4
|
+
includeEnv?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export type { IgnoreMatcher };
|
|
7
|
+
/**
|
|
8
|
+
* Non-destructively merge canonical rules into a local .gitignore.
|
|
9
|
+
* Existing local rules are preserved; only missing canonical rules are appended.
|
|
10
|
+
*/
|
|
11
|
+
export declare function mergeGitignoreRules(localContent: string, canonicalContent: string): GitignoreMergeResult;
|
|
12
|
+
/**
|
|
13
|
+
* Sync local .gitignore with canonical rules when enabled.
|
|
14
|
+
*/
|
|
15
|
+
export declare function syncProjectGitignore(projectRoot: string, syncGitignore: boolean): Promise<GitignoreMergeResult>;
|
|
16
|
+
/**
|
|
17
|
+
* Build a gitignore matcher from merged project rules and hard-coded exclusions.
|
|
18
|
+
*/
|
|
19
|
+
export declare function buildIgnoreMatcher(mergedGitignoreContent: string, options?: BuildIgnoreMatcherOptions): IgnoreMatcher;
|
|
20
|
+
export declare function isPathIgnored(matcher: IgnoreMatcher, projectRoot: string, absolutePath: string, isDirectory?: boolean): boolean;
|
|
21
|
+
//# sourceMappingURL=gitignore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gitignore.d.ts","sourceRoot":"","sources":["../../src/pack/gitignore.ts"],"names":[],"mappings":"AAIA,OAAO,EAAuB,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAC9E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAIvD,MAAM,WAAW,yBAAyB;IACxC,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,YAAY,EAAE,aAAa,EAAE,CAAC;AA2B9B;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,MAAM,EACpB,gBAAgB,EAAE,MAAM,GACvB,oBAAoB,CAuCtB;AAQD;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,OAAO,GACrB,OAAO,CAAC,oBAAoB,CAAC,CAoB/B;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,sBAAsB,EAAE,MAAM,EAC9B,OAAO,GAAE,yBAA8B,GACtC,aAAa,CAyBf;AAED,wBAAgB,aAAa,CAC3B,OAAO,EAAE,aAAa,EACtB,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,WAAW,GAAE,OAAe,GAC3B,OAAO,CAMT"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
import { loadCanonicalGitignore } from './canonical-gitignore.js';
|
|
4
|
+
import { ENV_EXCLUDED_PATTERNS, HARD_EXCLUDED_PATTERNS } from './exclusions.js';
|
|
5
|
+
import { createIgnoreMatcher } from './ignore-matcher.js';
|
|
6
|
+
const GIT_DIR_PATTERN = '.git/';
|
|
7
|
+
function normalizeRuleLine(line) {
|
|
8
|
+
const trimmed = line.trim();
|
|
9
|
+
if (!trimmed || trimmed.startsWith('#')) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
return trimmed;
|
|
13
|
+
}
|
|
14
|
+
function parseRules(content) {
|
|
15
|
+
return content
|
|
16
|
+
.split(/\r?\n/)
|
|
17
|
+
.map(normalizeRuleLine)
|
|
18
|
+
.filter((line) => line !== null);
|
|
19
|
+
}
|
|
20
|
+
function isEnvRule(rule) {
|
|
21
|
+
return ENV_EXCLUDED_PATTERNS.some((pattern) => {
|
|
22
|
+
if (pattern === rule)
|
|
23
|
+
return true;
|
|
24
|
+
if (pattern === '.env.*.local') {
|
|
25
|
+
return /^\.env\..+\.local$/.test(rule) || rule === '.env.*.local';
|
|
26
|
+
}
|
|
27
|
+
return rule === pattern || rule.startsWith(`${pattern}/`);
|
|
28
|
+
}) || rule === '.env' || rule.startsWith('.env.');
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Non-destructively merge canonical rules into a local .gitignore.
|
|
32
|
+
* Existing local rules are preserved; only missing canonical rules are appended.
|
|
33
|
+
*/
|
|
34
|
+
export function mergeGitignoreRules(localContent, canonicalContent) {
|
|
35
|
+
const localRules = parseRules(localContent);
|
|
36
|
+
const canonicalRules = parseRules(canonicalContent);
|
|
37
|
+
const existing = new Set(localRules);
|
|
38
|
+
const addedRules = [];
|
|
39
|
+
for (const rule of canonicalRules) {
|
|
40
|
+
if (!existing.has(rule)) {
|
|
41
|
+
addedRules.push(rule);
|
|
42
|
+
existing.add(rule);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (!existing.has(GIT_DIR_PATTERN)) {
|
|
46
|
+
addedRules.push(GIT_DIR_PATTERN);
|
|
47
|
+
existing.add(GIT_DIR_PATTERN);
|
|
48
|
+
}
|
|
49
|
+
const newlyAddedRules = addedRules.filter((rule) => !localRules.includes(rule));
|
|
50
|
+
const updated = newlyAddedRules.length > 0;
|
|
51
|
+
if (!updated) {
|
|
52
|
+
const normalized = localContent.endsWith('\n') || localContent.length === 0
|
|
53
|
+
? localContent
|
|
54
|
+
: `${localContent}\n`;
|
|
55
|
+
return {
|
|
56
|
+
mergedContent: normalized,
|
|
57
|
+
addedRules: [],
|
|
58
|
+
updated: false,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
const mergedContent = rebuildGitignoreContent(localContent, newlyAddedRules);
|
|
62
|
+
return {
|
|
63
|
+
mergedContent,
|
|
64
|
+
addedRules: newlyAddedRules,
|
|
65
|
+
updated: true,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function rebuildGitignoreContent(localContent, appendedRules) {
|
|
69
|
+
const base = localContent.trimEnd();
|
|
70
|
+
const suffix = appendedRules.join('\n');
|
|
71
|
+
return base.length > 0 ? `${base}\n\n# Added by nitrostack pack\n${suffix}\n` : `${suffix}\n`;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Sync local .gitignore with canonical rules when enabled.
|
|
75
|
+
*/
|
|
76
|
+
export async function syncProjectGitignore(projectRoot, syncGitignore) {
|
|
77
|
+
const gitignorePath = path.join(projectRoot, '.gitignore');
|
|
78
|
+
const canonicalContent = await loadCanonicalGitignore();
|
|
79
|
+
const localContent = (await fs.pathExists(gitignorePath))
|
|
80
|
+
? await fs.readFile(gitignorePath, 'utf-8')
|
|
81
|
+
: '';
|
|
82
|
+
const mergeResult = mergeGitignoreRules(localContent, canonicalContent);
|
|
83
|
+
if (syncGitignore && mergeResult.updated) {
|
|
84
|
+
await fs.writeFile(gitignorePath, mergeResult.mergedContent, 'utf-8');
|
|
85
|
+
return mergeResult;
|
|
86
|
+
}
|
|
87
|
+
// Matcher still uses merged rules, but only report an update when we actually wrote.
|
|
88
|
+
return {
|
|
89
|
+
mergedContent: mergeResult.mergedContent,
|
|
90
|
+
addedRules: [],
|
|
91
|
+
updated: false,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Build a gitignore matcher from merged project rules and hard-coded exclusions.
|
|
96
|
+
*/
|
|
97
|
+
export function buildIgnoreMatcher(mergedGitignoreContent, options = {}) {
|
|
98
|
+
const includeEnv = options.includeEnv ?? false;
|
|
99
|
+
let mergedRules = parseRules(mergedGitignoreContent);
|
|
100
|
+
if (includeEnv) {
|
|
101
|
+
mergedRules = mergedRules.filter((rule) => !isEnvRule(rule));
|
|
102
|
+
}
|
|
103
|
+
const hardPatterns = includeEnv
|
|
104
|
+
? HARD_EXCLUDED_PATTERNS.filter((pattern) => !ENV_EXCLUDED_PATTERNS.includes(pattern))
|
|
105
|
+
: HARD_EXCLUDED_PATTERNS;
|
|
106
|
+
const allRules = [...mergedRules];
|
|
107
|
+
for (const pattern of hardPatterns) {
|
|
108
|
+
if (!allRules.includes(pattern)) {
|
|
109
|
+
allRules.push(pattern);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (!allRules.includes(GIT_DIR_PATTERN)) {
|
|
113
|
+
allRules.push(GIT_DIR_PATTERN);
|
|
114
|
+
}
|
|
115
|
+
return createIgnoreMatcher(allRules);
|
|
116
|
+
}
|
|
117
|
+
export function isPathIgnored(matcher, projectRoot, absolutePath, isDirectory = false) {
|
|
118
|
+
const relativePath = path.relative(projectRoot, absolutePath).split(path.sep).join('/');
|
|
119
|
+
if (!relativePath || relativePath === '.') {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
return matcher.ignores(relativePath, isDirectory);
|
|
123
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lightweight gitignore-style matcher implemented in-house (no external ignore package).
|
|
3
|
+
* Supports the pattern subset used by nitrostack pack:
|
|
4
|
+
* - Directory rules: dist/, .git/
|
|
5
|
+
* - Recursive directory rules: slash-star-star/dist/
|
|
6
|
+
* - Exact file names: .env, .DS_Store
|
|
7
|
+
* - Simple globs: *.log, *.tsbuildinfo, npm-debug.log*, .env.*.local
|
|
8
|
+
* - Path-anchored rules with / (match relative to project root)
|
|
9
|
+
* - Negation with leading ! (last matching rule wins)
|
|
10
|
+
*/
|
|
11
|
+
export interface IgnoreMatcher {
|
|
12
|
+
ignores(relativePath: string, isDirectory?: boolean): boolean;
|
|
13
|
+
add(patterns: string | string[]): IgnoreMatcher;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Create a matcher from a list of gitignore-style patterns.
|
|
17
|
+
*/
|
|
18
|
+
export declare function createIgnoreMatcher(patterns?: string[]): IgnoreMatcher;
|
|
19
|
+
//# sourceMappingURL=ignore-matcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ignore-matcher.d.ts","sourceRoot":"","sources":["../../src/pack/ignore-matcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,YAAY,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;IAC9D,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,aAAa,CAAC;CACjD;AAkHD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,GAAE,MAAM,EAAO,GAAG,aAAa,CAmD1E"}
|