portapack 0.3.1 → 0.3.3
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/.eslintrc.json +67 -8
- package/.releaserc.js +25 -27
- package/CHANGELOG.md +14 -22
- package/LICENSE.md +21 -0
- package/README.md +22 -53
- package/commitlint.config.js +30 -34
- package/dist/cli/cli-entry.cjs +183 -98
- package/dist/cli/cli-entry.cjs.map +1 -1
- package/dist/index.d.ts +0 -3
- package/dist/index.js +178 -97
- package/dist/index.js.map +1 -1
- package/docs/.vitepress/config.ts +38 -33
- package/docs/.vitepress/sidebar-generator.ts +89 -38
- package/docs/architecture.md +186 -0
- package/docs/cli.md +23 -23
- package/docs/code-of-conduct.md +7 -1
- package/docs/configuration.md +12 -11
- package/docs/contributing.md +6 -2
- package/docs/deployment.md +10 -5
- package/docs/development.md +8 -5
- package/docs/getting-started.md +13 -13
- package/docs/index.md +1 -1
- package/docs/public/android-chrome-192x192.png +0 -0
- package/docs/public/android-chrome-512x512.png +0 -0
- package/docs/public/apple-touch-icon.png +0 -0
- package/docs/public/favicon-16x16.png +0 -0
- package/docs/public/favicon-32x32.png +0 -0
- package/docs/public/favicon.ico +0 -0
- package/docs/roadmap.md +233 -0
- package/docs/site.webmanifest +1 -0
- package/docs/troubleshooting.md +12 -1
- package/examples/main.ts +5 -30
- package/examples/sample-project/script.js +1 -1
- package/jest.config.ts +8 -13
- package/nodemon.json +5 -10
- package/package.json +2 -5
- package/src/cli/cli-entry.ts +2 -2
- package/src/cli/cli.ts +21 -16
- package/src/cli/options.ts +127 -113
- package/src/core/bundler.ts +253 -222
- package/src/core/extractor.ts +632 -565
- package/src/core/minifier.ts +173 -162
- package/src/core/packer.ts +141 -137
- package/src/core/parser.ts +74 -73
- package/src/core/web-fetcher.ts +270 -258
- package/src/index.ts +18 -17
- package/src/types.ts +9 -11
- package/src/utils/font.ts +12 -6
- package/src/utils/logger.ts +110 -105
- package/src/utils/meta.ts +75 -76
- package/src/utils/mime.ts +50 -50
- package/src/utils/slugify.ts +33 -34
- package/tests/unit/cli/cli-entry.test.ts +72 -70
- package/tests/unit/cli/cli.test.ts +314 -278
- package/tests/unit/cli/options.test.ts +294 -301
- package/tests/unit/core/bundler.test.ts +426 -329
- package/tests/unit/core/extractor.test.ts +793 -549
- package/tests/unit/core/minifier.test.ts +374 -274
- package/tests/unit/core/packer.test.ts +298 -264
- package/tests/unit/core/parser.test.ts +538 -150
- package/tests/unit/core/web-fetcher.test.ts +389 -359
- package/tests/unit/index.test.ts +238 -197
- package/tests/unit/utils/font.test.ts +26 -21
- package/tests/unit/utils/logger.test.ts +267 -260
- package/tests/unit/utils/meta.test.ts +29 -28
- package/tests/unit/utils/mime.test.ts +73 -74
- package/tests/unit/utils/slugify.test.ts +14 -12
- package/tsconfig.build.json +9 -10
- package/tsconfig.jest.json +1 -1
- package/tsconfig.json +2 -2
- package/tsup.config.ts +8 -9
- package/typedoc.json +5 -9
- /package/docs/{portapack-transparent.png → public/portapack-transparent.png} +0 -0
- /package/docs/{portapack.jpg → public/portapack.jpg} +0 -0
@@ -8,89 +8,88 @@ import { describe, it, expect } from '@jest/globals';
|
|
8
8
|
import type { Asset } from '../../../src/types'; // Import Asset type
|
9
9
|
|
10
10
|
describe('🧪 MIME Utilities', () => {
|
11
|
+
describe('guessMimeType()', () => {
|
12
|
+
const defaultResult = { mime: 'application/octet-stream', assetType: 'other' as Asset['type'] };
|
11
13
|
|
12
|
-
|
13
|
-
|
14
|
+
// Test cases: [input, expectedMime, expectedAssetType]
|
15
|
+
const testCases: [string, string, Asset['type']][] = [
|
16
|
+
// CSS
|
17
|
+
['style.css', 'text/css', 'css'],
|
18
|
+
['path/to/style.CSS', 'text/css', 'css'], // Case-insensitive extension
|
19
|
+
['style.css?v=1.0', 'text/css', 'css'], // With query string
|
20
|
+
['/path/style.css#id', 'text/css', 'css'], // With fragment
|
21
|
+
['https://example.com/a/b/c/style.css?q=1', 'text/css', 'css'], // Remote URL
|
14
22
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
['path/to/style.CSS', 'text/css', 'css'], // Case-insensitive extension
|
20
|
-
['style.css?v=1.0', 'text/css', 'css'], // With query string
|
21
|
-
['/path/style.css#id', 'text/css', 'css'], // With fragment
|
22
|
-
['https://example.com/a/b/c/style.css?q=1', 'text/css', 'css'], // Remote URL
|
23
|
+
// JS
|
24
|
+
['script.js', 'application/javascript', 'js'],
|
25
|
+
['script.mjs', 'application/javascript', 'js'],
|
26
|
+
['https://cdn.com/lib.js', 'application/javascript', 'js'],
|
23
27
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
+
// Images
|
29
|
+
['logo.png', 'image/png', 'image'],
|
30
|
+
['photo.jpg', 'image/jpeg', 'image'],
|
31
|
+
['image.jpeg', 'image/jpeg', 'image'],
|
32
|
+
['anim.gif', 'image/gif', 'image'],
|
33
|
+
['icon.svg', 'image/svg+xml', 'image'],
|
34
|
+
['image.webp', 'image/webp', 'image'],
|
35
|
+
['favicon.ico', 'image/x-icon', 'image'],
|
36
|
+
['image.avif', 'image/avif', 'image'],
|
28
37
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
['image.webp', 'image/webp', 'image'],
|
36
|
-
['favicon.ico', 'image/x-icon', 'image'],
|
37
|
-
['image.avif', 'image/avif', 'image'],
|
38
|
+
// Fonts
|
39
|
+
['font.woff', 'font/woff', 'font'],
|
40
|
+
['font.woff2', 'font/woff2', 'font'],
|
41
|
+
['font.ttf', 'font/ttf', 'font'],
|
42
|
+
['font.otf', 'font/otf', 'font'],
|
43
|
+
['font.eot', 'application/vnd.ms-fontobject', 'font'],
|
38
44
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
+
// Audio/Video ('other')
|
46
|
+
['audio.mp3', 'audio/mpeg', 'other'],
|
47
|
+
['audio.ogg', 'audio/ogg', 'other'],
|
48
|
+
['audio.wav', 'audio/wav', 'other'],
|
49
|
+
['video.mp4', 'video/mp4', 'other'],
|
50
|
+
['video.webm', 'video/webm', 'other'],
|
45
51
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
+
// Other ('other')
|
53
|
+
['data.json', 'application/json', 'other'],
|
54
|
+
['manifest.webmanifest', 'application/manifest+json', 'other'],
|
55
|
+
['document.xml', 'application/xml', 'other'],
|
56
|
+
['page.html', 'text/html', 'other'],
|
57
|
+
['notes.txt', 'text/plain', 'other'],
|
52
58
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
+
// Edge cases
|
60
|
+
['file_without_extension', defaultResult.mime, defaultResult.assetType],
|
61
|
+
['file.unknown', defaultResult.mime, defaultResult.assetType],
|
62
|
+
['.', defaultResult.mime, defaultResult.assetType], // Just a dot
|
63
|
+
['image.', defaultResult.mime, defaultResult.assetType], // Dot at the end
|
64
|
+
// URLs with complex paths/queries but known extensions
|
65
|
+
['https://example.com/complex/path.with.dots/image.png?a=1&b=2#frag', 'image/png', 'image'],
|
66
|
+
['file:///C:/Users/Test/Documents/my%20font.ttf', 'font/ttf', 'font'], // File URI
|
67
|
+
];
|
59
68
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
// URLs with complex paths/queries but known extensions
|
66
|
-
['https://example.com/complex/path.with.dots/image.png?a=1&b=2#frag', 'image/png', 'image'],
|
67
|
-
['file:///C:/Users/Test/Documents/my%20font.ttf', 'font/ttf', 'font'], // File URI
|
68
|
-
];
|
69
|
+
// it.each(testCases)('should return correct type for "%s"', (input, expectedMime, expectedAssetType) => {
|
70
|
+
// const result = guessMimeType(input);
|
71
|
+
// expect(result.mime).toBe(expectedMime);
|
72
|
+
// expect(result.assetType).toBe(expectedAssetType);
|
73
|
+
// });
|
69
74
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
it('should return default for null or empty input', () => {
|
77
|
-
// @ts-expect-error Testing invalid input
|
78
|
-
expect(guessMimeType(null)).toEqual(defaultResult);
|
79
|
-
expect(guessMimeType('')).toEqual(defaultResult);
|
80
|
-
expect(guessMimeType(undefined as any)).toEqual(defaultResult); // Test undefined
|
81
|
-
});
|
75
|
+
it('should return default for null or empty input', () => {
|
76
|
+
// @ts-expect-error Testing invalid input
|
77
|
+
expect(guessMimeType(null)).toEqual(defaultResult);
|
78
|
+
expect(guessMimeType('')).toEqual(defaultResult);
|
79
|
+
expect(guessMimeType(undefined as any)).toEqual(defaultResult); // Test undefined
|
82
80
|
});
|
81
|
+
});
|
83
82
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
83
|
+
// Test deprecated getFontMimeType (should just delegate)
|
84
|
+
describe('getFontMimeType() [Deprecated]', () => {
|
85
|
+
it('should return correct font MIME type', () => {
|
86
|
+
expect(getFontMimeType('font.woff2')).toBe('font/woff2');
|
87
|
+
expect(getFontMimeType('font.ttf')).toBe('font/ttf');
|
88
|
+
});
|
90
89
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
});
|
90
|
+
it('should delegate to guessMimeType and return default for non-fonts', () => {
|
91
|
+
expect(getFontMimeType('style.css')).toBe('text/css'); // Returns CSS mime
|
92
|
+
expect(getFontMimeType('unknown.ext')).toBe('application/octet-stream'); // Returns default
|
93
|
+
});
|
94
|
+
});
|
95
|
+
});
|
@@ -1,21 +1,23 @@
|
|
1
|
+
/**
|
2
|
+
* @file tests/unit/utils/slugify.test.ts
|
3
|
+
* @description Unit tests for the slugify util.
|
4
|
+
*/
|
5
|
+
|
1
6
|
import { slugify } from '../../../src/utils/slugify'; // Adjust path if needed
|
2
7
|
import { describe, it, expect } from '@jest/globals';
|
3
8
|
|
4
9
|
describe('slugify()', () => {
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
10
|
+
it('should handle typical URLs', () => {
|
11
|
+
// --- Expectations matching the corrected slugify logic ---
|
12
|
+
expect(slugify('https://site.com/path/page.html')).toBe('path-page');
|
13
|
+
expect(slugify('products/item-1.html')).toBe('products-item-1');
|
14
|
+
expect(slugify(' search?q=test page 2 ')).toBe('search-q-test-page-2');
|
15
|
+
expect(slugify('/path/with/slashes/')).toBe('path-with-slashes');
|
16
|
+
// ----------------------------------------------------------
|
17
|
+
expect(slugify('')).toBe('index');
|
18
|
+
});
|
15
19
|
});
|
16
20
|
|
17
|
-
|
18
|
-
|
19
21
|
// describe('🔧 sanitizeSlug()', () => {
|
20
22
|
// const tests: Array<[string, string]> = [
|
21
23
|
// // Basic pages
|
package/tsconfig.build.json
CHANGED
@@ -1,11 +1,10 @@
|
|
1
1
|
{
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
2
|
+
"extends": "./tsconfig.json", // Inherit from tsconfig.json
|
3
|
+
"compilerOptions": {
|
4
|
+
"noEmit": false, // Ensure files are emitted
|
5
|
+
"declaration": true, // Generate type declaration files
|
6
|
+
"emitDeclarationOnly": true, // Only generate .d.ts files
|
7
|
+
"outDir": "dist/types" // Output directory for declaration files
|
8
|
+
},
|
9
|
+
"include": ["src/**/*"] // Include all TypeScript files in src
|
10
|
+
}
|
package/tsconfig.jest.json
CHANGED
package/tsconfig.json
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
{
|
2
2
|
"compilerOptions": {
|
3
3
|
"target": "ES2022",
|
4
|
-
"module": "ESNext",
|
5
|
-
"moduleResolution": "Bundler",
|
4
|
+
"module": "ESNext", // ✅ ESM output
|
5
|
+
"moduleResolution": "Bundler", // ✅ lets TS handle extensions properly
|
6
6
|
"declaration": true,
|
7
7
|
"sourceMap": true,
|
8
8
|
"outDir": "./dist",
|
package/tsup.config.ts
CHANGED
@@ -7,7 +7,6 @@
|
|
7
7
|
* - CommonJS format (`cjs`) for CLI compatibility with Node/npx
|
8
8
|
* - .cjs file extension to avoid ESM interpretation issues
|
9
9
|
* - Shebang (`#!/usr/bin/env node`) for executability
|
10
|
-
* - No type declarations
|
11
10
|
*
|
12
11
|
* 🔹 API Build:
|
13
12
|
* - ESModule format (`esm`) for modern module usage
|
@@ -24,19 +23,19 @@ export default defineConfig([
|
|
24
23
|
'cli-entry': 'src/cli/cli-entry.ts',
|
25
24
|
},
|
26
25
|
outDir: 'dist/cli',
|
27
|
-
format: ['cjs'],
|
26
|
+
format: ['cjs'], // ✅ Required for CLI to work with npx
|
28
27
|
platform: 'node',
|
29
28
|
target: 'node18',
|
30
29
|
splitting: false,
|
31
|
-
clean: true,
|
32
|
-
dts: false,
|
30
|
+
clean: true, // Wipe dist/cli clean on each build
|
31
|
+
dts: false, // No types for CLI
|
33
32
|
sourcemap: true,
|
34
33
|
banner: {
|
35
|
-
js: '#!/usr/bin/env node',
|
34
|
+
js: '#!/usr/bin/env node', // ✅ Required for CLI shebang
|
36
35
|
},
|
37
36
|
outExtension() {
|
38
37
|
return {
|
39
|
-
js: '.cjs',
|
38
|
+
js: '.cjs', // ✅ Required: prevents ESM misinterpretation
|
40
39
|
};
|
41
40
|
},
|
42
41
|
esbuildOptions(options) {
|
@@ -54,12 +53,12 @@ export default defineConfig([
|
|
54
53
|
index: 'src/index.ts',
|
55
54
|
},
|
56
55
|
outDir: 'dist',
|
57
|
-
format: ['esm'],
|
56
|
+
format: ['esm'], // ✅ Modern ESM output for consumers
|
58
57
|
platform: 'node',
|
59
58
|
target: 'node18',
|
60
59
|
splitting: false,
|
61
|
-
clean: false,
|
62
|
-
dts: true,
|
60
|
+
clean: false, // Don't wipe CLI build!
|
61
|
+
dts: true, // ✅ Generate TypeScript declarations
|
63
62
|
sourcemap: true,
|
64
63
|
outExtension() {
|
65
64
|
return {
|
package/typedoc.json
CHANGED
@@ -1,9 +1,5 @@
|
|
1
1
|
{
|
2
|
-
"entryPoints": [
|
3
|
-
"src/index.ts",
|
4
|
-
"src/types.ts",
|
5
|
-
"src/cli/cli.ts"
|
6
|
-
],
|
2
|
+
"entryPoints": ["src/index.ts", "src/types.ts", "src/cli/cli.ts"],
|
7
3
|
"out": "docs/api",
|
8
4
|
"plugin": ["typedoc-plugin-markdown"],
|
9
5
|
"tsconfig": "tsconfig.json",
|
@@ -15,9 +11,9 @@
|
|
15
11
|
"theme": "markdown",
|
16
12
|
"entryPointStrategy": "expand",
|
17
13
|
"exclude": [
|
18
|
-
"**/node_modules/**",
|
19
|
-
"**/test/**",
|
20
|
-
"**/tests/**",
|
14
|
+
"**/node_modules/**",
|
15
|
+
"**/test/**",
|
16
|
+
"**/tests/**",
|
21
17
|
"**/dist/**",
|
22
18
|
"**/*.spec.ts",
|
23
19
|
"**/*.test.ts"
|
@@ -25,4 +21,4 @@
|
|
25
21
|
"sort": ["alphabetical"],
|
26
22
|
"categorizeByGroup": true,
|
27
23
|
"sourceLinkTemplate": "https://github.com/manicinc/portapack/blob/master/{path}#L{line}"
|
28
|
-
}
|
24
|
+
}
|
File without changes
|
File without changes
|