ship18ion 1.1.3 → 1.2.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/CONTRIBUTING.md +60 -0
- package/README.md +16 -0
- package/dist/engine/runner.js +9 -0
- package/dist/reporters/console.js +3 -0
- package/dist/rules/hygiene.js +47 -0
- package/dist/rules/packages.js +32 -0
- package/package.json +1 -1
- package/src/engine/runner.ts +9 -0
- package/src/reporters/console.ts +3 -0
- package/src/rules/hygiene.ts +52 -0
- package/src/rules/packages.ts +33 -0
- package/SHIPPING.md +0 -57
- package/walkthrough.md +0 -51
package/CONTRIBUTING.md
ADDED
|
@@ -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/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
|
|
package/dist/engine/runner.js
CHANGED
|
@@ -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)
|
|
@@ -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,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,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
|
+
}
|
package/package.json
CHANGED
package/src/engine/runner.ts
CHANGED
|
@@ -5,6 +5,8 @@ import { checkEnvVars } from '../rules/env';
|
|
|
5
5
|
import { checkSecrets } from '../rules/secrets';
|
|
6
6
|
import { checkSecurity } from '../rules/security';
|
|
7
7
|
import { checkDependencies, checkBuild } from '../rules/build';
|
|
8
|
+
import { checkHygiene } from '../rules/hygiene';
|
|
9
|
+
import { checkPackages } from '../rules/packages';
|
|
8
10
|
import { checkNextJs } from '../rules/frameworks/nextjs';
|
|
9
11
|
import { checkGit } from '../rules/git';
|
|
10
12
|
|
|
@@ -40,6 +42,13 @@ export async function runChecks(
|
|
|
40
42
|
if (onProgress) onProgress('Inspecting build artifacts...');
|
|
41
43
|
results.push(...await checkBuild(ctx));
|
|
42
44
|
|
|
45
|
+
// New Rules
|
|
46
|
+
if (onProgress) onProgress('Checking code hygiene...');
|
|
47
|
+
results.push(...await checkHygiene(ctx));
|
|
48
|
+
|
|
49
|
+
if (onProgress) onProgress('Validating packages...');
|
|
50
|
+
results.push(...await checkPackages(ctx));
|
|
51
|
+
|
|
43
52
|
// Framework specific checks
|
|
44
53
|
if (framework === 'nextjs') {
|
|
45
54
|
if (onProgress) onProgress('Running Next.js specific checks...');
|
package/src/reporters/console.ts
CHANGED
|
@@ -8,6 +8,9 @@ const CATEGORIES: Record<string, { icon: string; label: string }> = {
|
|
|
8
8
|
'security': { icon: '⚠️', label: 'Security' },
|
|
9
9
|
'dep': { icon: '📦', label: 'Dependency & Build' },
|
|
10
10
|
'build': { icon: '📦', label: 'Dependency & Build' },
|
|
11
|
+
'git': { icon: '🐙', label: 'Git & Repo' },
|
|
12
|
+
'hygiene': { icon: '🧹', label: 'Code Hygiene' },
|
|
13
|
+
'package': { icon: '📦', label: 'Packages' },
|
|
11
14
|
};
|
|
12
15
|
|
|
13
16
|
function getCategory(ruleId: string) {
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import { RuleContext, RuleResult } from '../engine/types';
|
|
3
|
+
|
|
4
|
+
export async function checkHygiene(ctx: RuleContext): Promise<RuleResult[]> {
|
|
5
|
+
const results: RuleResult[] = [];
|
|
6
|
+
|
|
7
|
+
const codeFiles = ctx.files.filter(f =>
|
|
8
|
+
f.match(/\.(js|ts|jsx|tsx)$/) &&
|
|
9
|
+
!f.includes('.test.') &&
|
|
10
|
+
!f.includes('.spec.')
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
for (const file of codeFiles) {
|
|
14
|
+
const content = fs.readFileSync(file, 'utf-8');
|
|
15
|
+
const lines = content.split('\n');
|
|
16
|
+
|
|
17
|
+
lines.forEach((line, index) => {
|
|
18
|
+
const lineNum = index + 1;
|
|
19
|
+
|
|
20
|
+
// 1. Console Log Check
|
|
21
|
+
// Allow console.error and console.warn, but warn on console.log
|
|
22
|
+
if (line.includes('console.log(')) {
|
|
23
|
+
// Ignore if commented out
|
|
24
|
+
if (!line.trim().startsWith('//') && !line.trim().startsWith('*')) {
|
|
25
|
+
results.push({
|
|
26
|
+
status: 'warn',
|
|
27
|
+
message: 'Leftover console.log() call detected.',
|
|
28
|
+
ruleId: 'hygiene-console-log',
|
|
29
|
+
file,
|
|
30
|
+
line: lineNum
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// 2. TODO / FIXME Check
|
|
36
|
+
if (line.match(/\/\/\s*(TODO|FIXME):/i)) {
|
|
37
|
+
|
|
38
|
+
if (line.match(/FIXME/i)) {
|
|
39
|
+
results.push({
|
|
40
|
+
status: 'warn',
|
|
41
|
+
message: 'FIXME comment found. Resolve before shipping.',
|
|
42
|
+
ruleId: 'hygiene-fixme',
|
|
43
|
+
file,
|
|
44
|
+
line: lineNum
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return results;
|
|
52
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import { RuleContext, RuleResult } from '../engine/types';
|
|
3
|
+
|
|
4
|
+
export async function checkPackages(ctx: RuleContext): Promise<RuleResult[]> {
|
|
5
|
+
const results: RuleResult[] = [];
|
|
6
|
+
|
|
7
|
+
const packageJsons = ctx.files.filter(f => f.endsWith('package.json') && !f.includes('node_modules'));
|
|
8
|
+
|
|
9
|
+
for (const pkgFile of packageJsons) {
|
|
10
|
+
try {
|
|
11
|
+
const content = JSON.parse(fs.readFileSync(pkgFile, 'utf-8'));
|
|
12
|
+
const deps = Object.keys(content.dependencies || {});
|
|
13
|
+
const devDeps = Object.keys(content.devDependencies || {});
|
|
14
|
+
|
|
15
|
+
// Find intersection
|
|
16
|
+
const duplicates = deps.filter(d => devDeps.includes(d));
|
|
17
|
+
|
|
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
|
+
|
|
32
|
+
return results;
|
|
33
|
+
}
|
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/walkthrough.md
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
ship18ion - Production Readiness Inspector
|
|
2
|
-
I have successfully built ship18ion, a CLI tool to check for production readiness.
|
|
3
|
-
|
|
4
|
-
Features Implemented
|
|
5
|
-
1. Environment Variable Safety
|
|
6
|
-
Unused Variable Detection: Scans
|
|
7
|
-
|
|
8
|
-
.env
|
|
9
|
-
files and code to find variables defined but never used.
|
|
10
|
-
Missing Variable Detection: Identifies process.env.VAR usages that lack a corresponding definition in
|
|
11
|
-
|
|
12
|
-
.env
|
|
13
|
-
(or config).
|
|
14
|
-
Format Support: Supports
|
|
15
|
-
|
|
16
|
-
.env
|
|
17
|
-
, .env.production files.
|
|
18
|
-
Robust AST Parsing: Correctly detects process.env.VAR, import.meta.env.VAR (Vite), and process.env["VAR"].
|
|
19
|
-
2. Secrets Detection
|
|
20
|
-
Pattern Matching: Detects AWS Keys, Google API Keys, Stripe Keys, and generic private keys.
|
|
21
|
-
Entropy Heuristics: Detects potential high-entropy strings assigned to "secret" or "key" variables.
|
|
22
|
-
3. Framework & Security Checks
|
|
23
|
-
Next.js Safety: Scans for NEXT_PUBLIC_ variables that appear to contain secrets (e.g. NEXT_PUBLIC_SECRET_KEY).
|
|
24
|
-
Git Safety: Warns if deploying from a dirty working directory or a non-production branch.
|
|
25
|
-
Debug Mode: Alerts on debug: true.
|
|
26
|
-
CORS Wildcards: Fails if origin: '*' is detected.
|
|
27
|
-
Database Credentials: Detects hardcoded connection strings.
|
|
28
|
-
4. Dependency & Build Safety
|
|
29
|
-
Dev Dependencies: Warns if eslint or other dev tools are in dependencies.
|
|
30
|
-
Build Artifacts: Alerts if source maps (.map) or
|
|
31
|
-
|
|
32
|
-
.env
|
|
33
|
-
files are found in build directories.
|
|
34
|
-
Usage
|
|
35
|
-
# In your project root
|
|
36
|
-
npx ship18ion check
|
|
37
|
-
# CI Mode (minimal output)
|
|
38
|
-
npx ship18ion check --ci
|
|
39
|
-
How to Ship & Share
|
|
40
|
-
See
|
|
41
|
-
|
|
42
|
-
SHIPPING.md
|
|
43
|
-
for detailed instructions on:
|
|
44
|
-
|
|
45
|
-
Local Testing: Using npm link to test on your other projects instantly.
|
|
46
|
-
Publishing: Pushing to NPM so anyone can use npx ship18ion.
|
|
47
|
-
Architecture
|
|
48
|
-
CLI: Built with commander.
|
|
49
|
-
Engine: TypeScript-based rule engine.
|
|
50
|
-
Parsing: Babel-based AST parsing.
|
|
51
|
-
Config: ship18ion.config.json support.
|