ship18ion 1.1.3 → 1.2.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.
Files changed (45) hide show
  1. package/CONTRIBUTING.md +60 -0
  2. package/LICENSE +21 -0
  3. package/README.md +16 -0
  4. package/dist/cli/index.d.ts +2 -0
  5. package/dist/engine/ast.d.ts +6 -0
  6. package/dist/engine/config.d.ts +12 -0
  7. package/dist/engine/detector.d.ts +2 -0
  8. package/dist/engine/runner.d.ts +3 -0
  9. package/dist/engine/runner.js +9 -0
  10. package/dist/engine/scanner.d.ts +1 -0
  11. package/dist/engine/secrets.d.ts +6 -0
  12. package/{src/engine/types.ts → dist/engine/types.d.ts} +15 -17
  13. package/dist/reporters/console.d.ts +2 -0
  14. package/dist/reporters/console.js +3 -0
  15. package/dist/rules/build.d.ts +3 -0
  16. package/dist/rules/env.d.ts +2 -0
  17. package/dist/rules/frameworks/nextjs.d.ts +2 -0
  18. package/dist/rules/git.d.ts +2 -0
  19. package/dist/rules/hygiene.d.ts +2 -0
  20. package/dist/rules/hygiene.js +47 -0
  21. package/dist/rules/packages.d.ts +2 -0
  22. package/dist/rules/packages.js +32 -0
  23. package/dist/rules/secrets.d.ts +2 -0
  24. package/dist/rules/security.d.ts +2 -0
  25. package/package.json +9 -3
  26. package/SHIPPING.md +0 -57
  27. package/src/cli/index.ts +0 -56
  28. package/src/engine/ast.ts +0 -84
  29. package/src/engine/config.ts +0 -28
  30. package/src/engine/detector.ts +0 -27
  31. package/src/engine/runner.ts +0 -53
  32. package/src/engine/scanner.ts +0 -22
  33. package/src/engine/secrets.ts +0 -26
  34. package/src/reporters/console.ts +0 -66
  35. package/src/rules/build.ts +0 -77
  36. package/src/rules/env.ts +0 -99
  37. package/src/rules/frameworks/nextjs.ts +0 -33
  38. package/src/rules/git.ts +0 -95
  39. package/src/rules/secrets.ts +0 -53
  40. package/src/rules/security.ts +0 -55
  41. package/tests/fixtures/leaky-app/.env +0 -3
  42. package/tests/fixtures/leaky-app/package.json +0 -7
  43. package/tests/fixtures/leaky-app/src/index.js +0 -21
  44. package/tsconfig.json +0 -15
  45. package/walkthrough.md +0 -51
@@ -0,0 +1,60 @@
1
+ # Contributing to ship18ion
2
+
3
+ First off, thanks for taking the time to contribute! ❤️
4
+
5
+ All types of contributions are encouraged and valued. See the [Table of Contents](#table-of-contents) for different ways to help and details about how this project handles them. Please make sure to read the relevant section before making your contribution. It will make it a lot easier for us maintainers and smooth out the experience for all involved. The community looks forward to your contributions.
6
+
7
+ ## Table of Contents
8
+
9
+ - [I Have a Question](#i-have-a-question)
10
+ - [I Want To Contribute](#i-want-to-contribute)
11
+ - [Reporting Bugs](#reporting-bugs)
12
+ - [Suggesting Enhancements](#suggesting-enhancements)
13
+ - [Your First Code Contribution](#your-first-code-contribution)
14
+
15
+ ## I Have a Question
16
+
17
+ > If you want to ask a question, we assume that you have read the available [Documentation](README.md).
18
+
19
+ Before you ask a question, it is best to search for existing [Issues](/issues) that might help you. In case you have found a suitable issue and still need clarification, you can write your question in this issue. It is also advisable to search the internet for answers first.
20
+
21
+ ## I Want To Contribute
22
+
23
+ ### Reporting Bugs
24
+
25
+ - Make sure that you are using the latest version.
26
+ - Read the documentation carefully and find out if the functionality is already covered, maybe by an individual configuration.
27
+ - Perform a search to see if the problem has already been reported. If it has, add a comment to the existing issue instead of opening a new one.
28
+
29
+ ### Suggesting Enhancements
30
+
31
+ - Open a new issue and use the **Feature Request** template.
32
+ - Explain why this enhancement would be useful to most users.
33
+
34
+ ## Styleguides
35
+
36
+ ### Commit Messages
37
+
38
+ - Use [Conventional Commits](https://www.conventionalcommits.org/).
39
+
40
+ ### Code Style
41
+
42
+ - Keep code clean and use the existing ESLint/Prettier configs (if available).
43
+ - Add tests for new features.
44
+
45
+ ## Development Workflow
46
+
47
+ To test your changes locally on another project without publishing:
48
+
49
+ 1. **Build and Link**:
50
+ ```bash
51
+ npm run build
52
+ npm link
53
+ ```
54
+ 2. **Use in Target Project**:
55
+ ```bash
56
+ npm link ship18ion
57
+ npx ship18ion
58
+ ```
59
+
60
+ Thank you!
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 champ18ion
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -31,6 +31,17 @@ Think of it as `eslint` but for **deployability**.
31
31
  - Ensures `.env` files are not bundled into build output directories.
32
32
  - Checks for dev dependencies (like `eslint`) accidentally listed in `dependencies`.
33
33
 
34
+ - **🧹 Code Hygiene (New)**:
35
+ - Warns on leftover `console.log()` calls.
36
+ - Flags `FIXME` comments that need resolution.
37
+
38
+ - **📦 Dependencies (New)**:
39
+ - Checks for duplicate packages (listed in both `dependencies` and `devDependencies`).
40
+
41
+ - **🐙 Git Safety (New)**:
42
+ - Ensures critical files (`node_modules`, `.env`) and framework artifacts (`.next`) are git-ignored.
43
+ - **Security**: alerts if dangerous keys (e.g. `serviceAccountKey.json`) exist but are *not* ignored.
44
+
34
45
  - **👷 CI/CD Ready**:
35
46
  - Zero config by default.
36
47
  - Returns exit code `1` on failure to block bad builds.
@@ -95,6 +106,11 @@ npx ship18ion check --ci
95
106
  | **Next.js** | `nextjs-public-secret` | High-entropy string found in `NEXT_PUBLIC_` variable. |
96
107
  | **Security** | `security-cors` | Detects wildcard `Access-Control-Allow-Origin`. |
97
108
  | **Git** | `git-dirty` | Warns if deploying with uncommitted changes. |
109
+ | **Git** | `git-ignore-missing` | Warns if `.gitignore` is missing critical entries (`node_modules`, `.env`). |
110
+ | **Git** | `git-ignore-auth` | **Critical**: Fails if `serviceAccountKey.json` etc are not ignored. |
111
+ | **Hygiene** | `hygiene-console-log` | Warns on `console.log` in production code. |
112
+ | **Hygiene** | `hygiene-fixme` | Warns on leftover `FIXME` comments. |
113
+ | **Package** | `package-duplicate` | Warns if a package is in both dependency lists. |
98
114
 
99
115
  ## 🤝 Contributing
100
116
 
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,6 @@
1
+ export interface EnvUsage {
2
+ name: string;
3
+ line: number;
4
+ file: string;
5
+ }
6
+ export declare function findEnvUsages(filePath: string): EnvUsage[];
@@ -0,0 +1,12 @@
1
+ export interface Ship18ionConfig {
2
+ env?: {
3
+ required?: string[];
4
+ disallowed?: string[];
5
+ };
6
+ security?: {
7
+ noCorsWildcard?: boolean;
8
+ requireRateLimit?: boolean;
9
+ };
10
+ ignore?: string[];
11
+ }
12
+ export declare function loadConfig(cwd?: string): Promise<Ship18ionConfig>;
@@ -0,0 +1,2 @@
1
+ export type FrameworkType = 'nextjs' | 'remix' | 'vite' | 'nestjs' | 'express' | 'fastify' | 'Node.js / Generic' | 'unknown';
2
+ export declare function detectFramework(cwd: string): Promise<FrameworkType>;
@@ -0,0 +1,3 @@
1
+ import { Ship18ionConfig } from './config';
2
+ import { RuleResult } from './types';
3
+ export declare function runChecks(config: Ship18ionConfig, cwd: string, onProgress?: (stage: string) => void): Promise<RuleResult[]>;
@@ -6,6 +6,8 @@ const env_1 = require("../rules/env");
6
6
  const secrets_1 = require("../rules/secrets");
7
7
  const security_1 = require("../rules/security");
8
8
  const build_1 = require("../rules/build");
9
+ const hygiene_1 = require("../rules/hygiene");
10
+ const packages_1 = require("../rules/packages");
9
11
  const nextjs_1 = require("../rules/frameworks/nextjs");
10
12
  const git_1 = require("../rules/git");
11
13
  const detector_1 = require("./detector");
@@ -33,6 +35,13 @@ async function runChecks(config, cwd, onProgress) {
33
35
  if (onProgress)
34
36
  onProgress('Inspecting build artifacts...');
35
37
  results.push(...await (0, build_1.checkBuild)(ctx));
38
+ // New Rules
39
+ if (onProgress)
40
+ onProgress('Checking code hygiene...');
41
+ results.push(...await (0, hygiene_1.checkHygiene)(ctx));
42
+ if (onProgress)
43
+ onProgress('Validating packages...');
44
+ results.push(...await (0, packages_1.checkPackages)(ctx));
36
45
  // Framework specific checks
37
46
  if (framework === 'nextjs') {
38
47
  if (onProgress)
@@ -0,0 +1 @@
1
+ export declare function scanFiles(cwd: string, ignore?: string[]): Promise<string[]>;
@@ -0,0 +1,6 @@
1
+ export declare const SECRET_PATTERNS: {
2
+ name: string;
3
+ regex: RegExp;
4
+ }[];
5
+ export declare function calculateEntropy(str: string): number;
6
+ export declare function isHighEntropy(str: string, threshold?: number): boolean;
@@ -1,17 +1,15 @@
1
- import { Ship18ionConfig } from './config';
2
- import { FrameworkType } from './detector';
3
-
4
- export interface RuleResult {
5
- status: 'pass' | 'fail' | 'warn';
6
- message: string;
7
- file?: string;
8
- line?: number;
9
- ruleId: string;
10
- }
11
-
12
- export interface RuleContext {
13
- config: Ship18ionConfig;
14
- files: string[];
15
- cwd: string;
16
- framework: FrameworkType;
17
- }
1
+ import { Ship18ionConfig } from './config';
2
+ import { FrameworkType } from './detector';
3
+ export interface RuleResult {
4
+ status: 'pass' | 'fail' | 'warn';
5
+ message: string;
6
+ file?: string;
7
+ line?: number;
8
+ ruleId: string;
9
+ }
10
+ export interface RuleContext {
11
+ config: Ship18ionConfig;
12
+ files: string[];
13
+ cwd: string;
14
+ framework: FrameworkType;
15
+ }
@@ -0,0 +1,2 @@
1
+ import { RuleResult } from '../engine/types';
2
+ export declare function reportConsole(results: RuleResult[], cwd: string, framework?: string): void;
@@ -12,6 +12,9 @@ const CATEGORIES = {
12
12
  'security': { icon: '⚠️', label: 'Security' },
13
13
  'dep': { icon: '📦', label: 'Dependency & Build' },
14
14
  'build': { icon: '📦', label: 'Dependency & Build' },
15
+ 'git': { icon: '🐙', label: 'Git & Repo' },
16
+ 'hygiene': { icon: '🧹', label: 'Code Hygiene' },
17
+ 'package': { icon: '📦', label: 'Packages' },
15
18
  };
16
19
  function getCategory(ruleId) {
17
20
  const prefix = ruleId.split('-')[0];
@@ -0,0 +1,3 @@
1
+ import { RuleContext, RuleResult } from '../engine/types';
2
+ export declare function checkDependencies(ctx: RuleContext): Promise<RuleResult[]>;
3
+ export declare function checkBuild(ctx: RuleContext): Promise<RuleResult[]>;
@@ -0,0 +1,2 @@
1
+ import { RuleContext, RuleResult } from '../engine/types';
2
+ export declare function checkEnvVars(ctx: RuleContext): Promise<RuleResult[]>;
@@ -0,0 +1,2 @@
1
+ import { RuleContext, RuleResult } from '../../engine/types';
2
+ export declare function checkNextJs(ctx: RuleContext): Promise<RuleResult[]>;
@@ -0,0 +1,2 @@
1
+ import { RuleContext, RuleResult } from '../engine/types';
2
+ export declare function checkGit(ctx: RuleContext): Promise<RuleResult[]>;
@@ -0,0 +1,2 @@
1
+ import { RuleContext, RuleResult } from '../engine/types';
2
+ export declare function checkHygiene(ctx: RuleContext): Promise<RuleResult[]>;
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.checkHygiene = checkHygiene;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ async function checkHygiene(ctx) {
9
+ const results = [];
10
+ const codeFiles = ctx.files.filter(f => f.match(/\.(js|ts|jsx|tsx)$/) &&
11
+ !f.includes('.test.') &&
12
+ !f.includes('.spec.'));
13
+ for (const file of codeFiles) {
14
+ const content = fs_1.default.readFileSync(file, 'utf-8');
15
+ const lines = content.split('\n');
16
+ lines.forEach((line, index) => {
17
+ const lineNum = index + 1;
18
+ // 1. Console Log Check
19
+ // Allow console.error and console.warn, but warn on console.log
20
+ if (line.includes('console.log(')) {
21
+ // Ignore if commented out
22
+ if (!line.trim().startsWith('//') && !line.trim().startsWith('*')) {
23
+ results.push({
24
+ status: 'warn',
25
+ message: 'Leftover console.log() call detected.',
26
+ ruleId: 'hygiene-console-log',
27
+ file,
28
+ line: lineNum
29
+ });
30
+ }
31
+ }
32
+ // 2. TODO / FIXME Check
33
+ if (line.match(/\/\/\s*(TODO|FIXME):/i)) {
34
+ if (line.match(/FIXME/i)) {
35
+ results.push({
36
+ status: 'warn',
37
+ message: 'FIXME comment found. Resolve before shipping.',
38
+ ruleId: 'hygiene-fixme',
39
+ file,
40
+ line: lineNum
41
+ });
42
+ }
43
+ }
44
+ });
45
+ }
46
+ return results;
47
+ }
@@ -0,0 +1,2 @@
1
+ import { RuleContext, RuleResult } from '../engine/types';
2
+ export declare function checkPackages(ctx: RuleContext): Promise<RuleResult[]>;
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.checkPackages = checkPackages;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ async function checkPackages(ctx) {
9
+ const results = [];
10
+ const packageJsons = ctx.files.filter(f => f.endsWith('package.json') && !f.includes('node_modules'));
11
+ for (const pkgFile of packageJsons) {
12
+ try {
13
+ const content = JSON.parse(fs_1.default.readFileSync(pkgFile, 'utf-8'));
14
+ const deps = Object.keys(content.dependencies || {});
15
+ const devDeps = Object.keys(content.devDependencies || {});
16
+ // Find intersection
17
+ const duplicates = deps.filter(d => devDeps.includes(d));
18
+ for (const dup of duplicates) {
19
+ results.push({
20
+ status: 'warn',
21
+ message: `Package '${dup}' is listed in both 'dependencies' and 'devDependencies'.`,
22
+ ruleId: 'package-duplicate',
23
+ file: pkgFile
24
+ });
25
+ }
26
+ }
27
+ catch (e) {
28
+ // ignore malformed json
29
+ }
30
+ }
31
+ return results;
32
+ }
@@ -0,0 +1,2 @@
1
+ import { RuleContext, RuleResult } from '../engine/types';
2
+ export declare function checkSecrets(ctx: RuleContext): Promise<RuleResult[]>;
@@ -0,0 +1,2 @@
1
+ import { RuleContext, RuleResult } from '../engine/types';
2
+ export declare function checkSecurity(ctx: RuleContext): Promise<RuleResult[]>;
package/package.json CHANGED
@@ -1,11 +1,17 @@
1
1
  {
2
2
  "name": "ship18ion",
3
- "version": "1.1.3",
3
+ "version": "1.2.1",
4
4
  "description": "",
5
5
  "main": "dist/cli/index.js",
6
6
  "bin": {
7
7
  "ship18ion": "./dist/cli/index.js"
8
8
  },
9
+ "files": [
10
+ "dist",
11
+ "README.md",
12
+ "CONTRIBUTING.md",
13
+ "LICENSE"
14
+ ],
9
15
  "scripts": {
10
16
  "build": "tsc",
11
17
  "prepublishOnly": "npm run build",
@@ -21,8 +27,8 @@
21
27
  "deployment",
22
28
  "linter"
23
29
  ],
24
- "author": "TRIPLE HASH",
25
- "license": "ISC",
30
+ "author": "champ18ion",
31
+ "license": "MIT",
26
32
  "dependencies": {
27
33
  "@babel/parser": "^7.28.5",
28
34
  "@babel/traverse": "^7.28.5",
package/SHIPPING.md DELETED
@@ -1,57 +0,0 @@
1
- # Shipping ship18ion
2
-
3
- ## 1. Local Testing (Link)
4
- To test `ship18ion` on your other local projects without publishing, use `npm link`.
5
-
6
- 1. **In this directory (ship18ion):**
7
- ```bash
8
- npm run build
9
- npm link
10
- ```
11
- This creates a global symlink to your local version.
12
-
13
- 2. **In your target project directory:**
14
- ```bash
15
- npm link ship18ion
16
- ```
17
- Now you can run:
18
- ```bash
19
- npx ship18ion check
20
- ```
21
-
22
- 3. **To unlink:**
23
- ```bash
24
- # In target project
25
- npm unlink -g ship18ion
26
-
27
- # In ship18ion directory
28
- npm unlink -g
29
- ```
30
-
31
- ## 2. Publishing to NPM
32
- To share this tool with the world.
33
-
34
- 1. **Login to NPM:**
35
- ```bash
36
- npm login
37
- ```
38
-
39
- 2. **Prepare:**
40
- - Ensure `package.json` has the correct version.
41
- - Run tests: `npm test` (or verifying script).
42
-
43
- 3. **Publish:**
44
- ```bash
45
- npm publish
46
- ```
47
-
48
- If you have a scoped package (e.g. `@yourname/ship18ion`), use:
49
- ```bash
50
- npm publish --access public
51
- ```
52
-
53
- ## 3. Usage for Users
54
- Once published, anyone can use it without installation:
55
- ```bash
56
- npx ship18ion@latest
57
- ```
package/src/cli/index.ts DELETED
@@ -1,56 +0,0 @@
1
- #!/usr/bin/env node
2
- import { Command } from 'commander';
3
- import chalk from 'chalk';
4
- import { loadConfig } from '../engine/config';
5
- import { runChecks } from '../engine/runner';
6
- import { reportConsole } from '../reporters/console';
7
-
8
- const program = new Command();
9
-
10
- import figlet from 'figlet';
11
- import gradient from 'gradient-string';
12
- import ora from 'ora';
13
- import { detectFramework } from '../engine/detector';
14
-
15
- program
16
- .command('check', { isDefault: true })
17
- .description('Run production readiness checks')
18
- .option('--ci', 'Run in CI mode (minimal output, exit codes)')
19
- .action(async (options) => {
20
- if (!options.ci) {
21
- console.log(gradient.pastel.multiline(figlet.textSync('SHIP18ION')));
22
- console.log(chalk.dim('Production Readiness Inspector\n'));
23
- }
24
-
25
- const cwd = process.cwd();
26
- const config = await loadConfig(cwd);
27
- const spinner = ora('Initializing...').start();
28
-
29
- try {
30
- let framework: string = 'unknown';
31
- if (!options.ci) {
32
- framework = await detectFramework(cwd);
33
- spinner.text = `Detected Framework: ${chalk.cyan(framework.toUpperCase())}`;
34
- await new Promise(r => setTimeout(r, 800)); // Brief pause to show framework
35
- } else {
36
- // Even in CI, simple detection is useful for reporting if needed, or we just skip
37
- framework = await detectFramework(cwd);
38
- }
39
-
40
- const results = await runChecks(config, cwd, (stage) => {
41
- if (!options.ci) spinner.text = stage;
42
- });
43
-
44
- spinner.succeed(chalk.green('Checks completed!'));
45
- console.log('');
46
-
47
- // Uses console reporter for both normal and CI for now (it handles exit codes)
48
- reportConsole(results, cwd, framework);
49
- } catch (e) {
50
- spinner.fail(chalk.red('Error running checks'));
51
- console.error(e);
52
- process.exit(1);
53
- }
54
- });
55
-
56
- program.parse(process.argv);
package/src/engine/ast.ts DELETED
@@ -1,84 +0,0 @@
1
- import fs from 'fs';
2
- import * as parser from '@babel/parser';
3
- import traverse from '@babel/traverse';
4
- import * as t from '@babel/types';
5
-
6
- export interface EnvUsage {
7
- name: string;
8
- line: number;
9
- file: string;
10
- }
11
-
12
- export function findEnvUsages(filePath: string): EnvUsage[] {
13
- if (!fs.existsSync(filePath)) return [];
14
-
15
- const code = fs.readFileSync(filePath, 'utf-8');
16
- const usages: EnvUsage[] = [];
17
-
18
- // Only parse JS/TS files
19
- if (!/\.(js|ts|jsx|tsx)$/.test(filePath)) {
20
- return [];
21
- }
22
-
23
- try {
24
- const ast = parser.parse(code, {
25
- sourceType: 'module',
26
- plugins: ['typescript', 'jsx'],
27
- });
28
-
29
- traverse(ast, {
30
- MemberExpression(path) {
31
- // 1. Check for process.env.VAR
32
- if (
33
- t.isMemberExpression(path.node.object) &&
34
- t.isIdentifier(path.node.object.object) &&
35
- path.node.object.object.name === 'process' &&
36
- t.isIdentifier(path.node.object.property) &&
37
- path.node.object.property.name === 'env'
38
- ) {
39
- if (t.isIdentifier(path.node.property)) {
40
- usages.push({
41
- name: path.node.property.name,
42
- line: path.node.loc?.start.line || 0,
43
- file: filePath
44
- });
45
- } else if (t.isStringLiteral(path.node.property)) {
46
- usages.push({
47
- name: path.node.property.value,
48
- line: path.node.loc?.start.line || 0,
49
- file: filePath
50
- });
51
- }
52
- }
53
-
54
- // 2. Check for import.meta.env.VAR (Vite)
55
- // AST structure: MemberExpression
56
- // object: MemberExpression
57
- // object: MetaProperty (import.meta)
58
- // property: Identifier (env)
59
- // property: Identifier (VAR)
60
-
61
- if (
62
- t.isMemberExpression(path.node.object) &&
63
- t.isMetaProperty(path.node.object.object) &&
64
- path.node.object.object.meta.name === 'import' &&
65
- path.node.object.object.property.name === 'meta' &&
66
- t.isIdentifier(path.node.object.property) &&
67
- path.node.object.property.name === 'env'
68
- ) {
69
- if (t.isIdentifier(path.node.property)) {
70
- usages.push({
71
- name: path.node.property.name,
72
- line: path.node.loc?.start.line || 0,
73
- file: filePath
74
- });
75
- }
76
- }
77
- }
78
- });
79
-
80
- } catch (e) {
81
- // console.warn(`Failed to parse ${filePath}:`, e);
82
- }
83
- return usages;
84
- }
@@ -1,28 +0,0 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
-
4
- export interface Ship18ionConfig {
5
- env?: {
6
- required?: string[];
7
- disallowed?: string[];
8
- };
9
- security?: {
10
- noCorsWildcard?: boolean;
11
- requireRateLimit?: boolean;
12
- };
13
- ignore?: string[];
14
- }
15
-
16
- export async function loadConfig(cwd: string = process.cwd()): Promise<Ship18ionConfig> {
17
- const configPath = path.join(cwd, 'ship18ion.config.json');
18
- if (fs.existsSync(configPath)) {
19
- const content = fs.readFileSync(configPath, 'utf-8');
20
- try {
21
- return JSON.parse(content);
22
- } catch (e) {
23
- console.error('Failed to parse config file:', e);
24
- return {};
25
- }
26
- }
27
- return {};
28
- }
@@ -1,27 +0,0 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
-
4
- export type FrameworkType = 'nextjs' | 'remix' | 'vite' | 'nestjs' | 'express' | 'fastify' | 'Node.js / Generic' | 'unknown';
5
-
6
- export async function detectFramework(cwd: string): Promise<FrameworkType> {
7
- const pkgPath = path.join(cwd, 'package.json');
8
- if (!fs.existsSync(pkgPath)) {
9
- return 'unknown';
10
- }
11
-
12
- try {
13
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
14
- const deps = { ...pkg.dependencies, ...pkg.devDependencies };
15
-
16
- if (deps['next']) return 'nextjs';
17
- if (deps['@remix-run/react']) return 'remix';
18
- if (deps['vite']) return 'vite';
19
- if (deps['@nestjs/core']) return 'nestjs';
20
- if (deps['express']) return 'express';
21
- if (deps['fastify']) return 'fastify';
22
-
23
- return 'Node.js / Generic';
24
- } catch (e) {
25
- return 'unknown';
26
- }
27
- }