portapack 0.2.1 → 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/jest.config.ts CHANGED
@@ -5,7 +5,8 @@ const config: Config = {
5
5
  /**
6
6
  * Use ts-jest with ESM support.
7
7
  */
8
- preset: 'ts-jest/presets/default-esm',
8
+ // preset: 'ts-jest/presets/default-esm',
9
+ preset: 'ts-jest',
9
10
 
10
11
  /**
11
12
  * Node test environment
@@ -20,16 +21,25 @@ const config: Config = {
20
21
  /**
21
22
  * Tell ts-jest to use ESM transformation
22
23
  */
24
+ // transform: {
25
+ // '^.+\\.tsx?$': [
26
+ // 'ts-jest',
27
+ // {
28
+ // useESM: true,
29
+ // tsconfig: './tsconfig.jest.json'
30
+ // }
31
+ // ]
32
+ // },
33
+
23
34
  transform: {
24
35
  '^.+\\.tsx?$': [
25
36
  'ts-jest',
26
37
  {
27
- useESM: true,
28
- tsconfig: './tsconfig.jest.json'
38
+ // useESM: false, // Explicitly false or remove this line entirely
39
+ tsconfig: './tsconfig.jest.json' // Ensure this tsconfig targets CommonJS
29
40
  }
30
41
  ]
31
42
  },
32
-
33
43
  /**
34
44
  * Treat `.ts` files as ESM modules.
35
45
  */
@@ -68,10 +78,10 @@ const config: Config = {
68
78
  */
69
79
  coverageThreshold: {
70
80
  global: {
71
- branches: 60,
72
- functions: 60,
73
- lines: 60,
74
- statements: 60,
81
+ branches: 70,
82
+ functions: 70,
83
+ lines: 70,
84
+ statements: 70,
75
85
  },
76
86
  },
77
87
 
package/jest.setup.cjs CHANGED
@@ -1,211 +1,131 @@
1
- /**
2
- * @file jest.setup.cjs
3
- * @description Jest global setup script executed before test suites run (per file).
4
- * Responsible for creating temporary directories for test outputs and fixtures,
5
- * setting up mock data (like the sample project), and cleaning up afterwards.
6
- * Uses CommonJS syntax as Jest setup files often run in a CJS context.
7
- */
8
-
9
- // Node.js core modules required for setup
10
- const fs = require('fs/promises'); // Using promises API for async operations
1
+ // jest.setup.cjs
2
+ const fs = require('fs/promises');
11
3
  const path = require('path');
12
4
  const crypto = require('crypto');
13
5
  const os = require('os');
6
+ // const jest = require('@jest/globals'); // REMOVE THIS LINE
14
7
 
15
- // Generate a unique ID for each test execution session.
16
- // This helps isolate test runs if running concurrently or prevents conflicts
17
- // if cleanup fails on a previous run.
18
8
  const TEST_RUN_ID = crypto.randomBytes(4).toString('hex');
19
-
20
- // Helper function for resolving paths relative to this setup file's directory
21
- // (usually the project root or a specific test config directory)
22
9
  const resolve = (...args) => path.resolve(__dirname, ...args);
23
10
 
24
- /**
25
- * Utility function to ensure a directory exists.
26
- * Creates the directory recursively if it doesn't exist.
27
- * Ignores 'EEXIST' error if the directory is already present.
28
- * @param {string} dir - The absolute path of the directory to ensure.
29
- * @returns {Promise<void>}
30
- * @throws {Error} Throws errors other than 'EEXIST' during directory creation.
31
- */
11
+ // Use the *original* console.log temporarily for setup debugging
12
+ const originalSetupLog = console.log;
13
+
32
14
  const ensureDir = async (dir) => {
33
15
  try {
34
16
  await fs.mkdir(dir, { recursive: true });
35
17
  } catch (e) {
36
- // If the error is simply that the directory already exists, ignore it.
37
18
  if (e.code !== 'EEXIST') {
38
- console.error(`Failed to ensure directory ${dir}:`, e); // Log unexpected errors
39
- throw e; // Re-throw other errors
19
+ originalSetupLog(`[SETUP-ERROR] Failed to ensure directory ${dir}:`, e);
20
+ throw e;
40
21
  }
41
22
  }
42
23
  };
43
24
 
44
- // Define the base temporary directory using the OS's temp location and the unique run ID.
45
- // Using os.tmpdir() is generally safer regarding permissions and OS conventions.
46
25
  const tempDir = path.join(os.tmpdir(), 'portapack-tests', TEST_RUN_ID);
47
-
48
- /**
49
- * Object containing base paths for various test-related directories.
50
- * These are constructed relative to the main temporary directory.
51
- */
26
+ // *** Make sure baseDirs is defined ***
27
+ // (Assuming it was defined correctly before the placeholder comment)
52
28
  const baseDirs = {
53
- root: resolve(), // Project root (where jest.config / setup file likely is)
54
- output: path.join(tempDir, 'test-output'), // General output for test artifacts
55
- fixtures: path.join(tempDir, '__fixtures__'), // Base for fixture data (if needed)
56
- fixturesOutput: path.join(tempDir, '__fixtures__/output'), // Output specifically related to fixtures
57
- sampleProject: path.join(tempDir, 'sample-project') // Directory for the mock HTML project
29
+ root: resolve(), // Project root
30
+ output: path.join(tempDir, 'test-output'), // General output
31
+ fixtures: path.join(tempDir, '__fixtures__'), // Base for fixtures
32
+ fixturesOutput: path.join(tempDir, '__fixtures__/output'), // Fixture output
33
+ sampleProject: path.join(tempDir, 'sample-project') // Mock project dir
58
34
  };
35
+ // **********************************
59
36
 
60
- /**
61
- * Jest's global beforeAll hook. Runs once before any tests in a suite file execute.
62
- * Sets up the necessary directory structure and global variables/mocks for tests.
63
- */
37
+
38
+ // --- Use Jest's GLOBAL beforeAll/afterAll/jest ---
39
+ // These are available globally in setup files
64
40
  beforeAll(async () => {
65
- console.log(`Setting up test environment in: ${tempDir}`); // Log setup start
41
+ originalSetupLog(`[SETUP] Starting setup in: ${tempDir}`);
66
42
 
67
- // Create essential output/fixture directories early.
68
- // Using Promise.all here is likely safe as they are independent paths.
43
+ originalSetupLog('[SETUP] Ensuring baseDirs...');
44
+ // Make sure baseDirs is defined before using it
45
+ if (!baseDirs || !baseDirs.output || !baseDirs.fixturesOutput) {
46
+ throw new Error("baseDirs is not properly defined!");
47
+ }
69
48
  await Promise.all([
70
49
  ensureDir(baseDirs.output),
71
50
  ensureDir(baseDirs.fixturesOutput)
72
51
  ]);
52
+ originalSetupLog('[SETUP] BaseDirs ensured.');
73
53
 
74
- // Define paths specific to this test run using the TEST_RUN_ID for further isolation.
54
+ originalSetupLog('[SETUP] Creating runDirs...');
75
55
  const runDirs = {
76
56
  uniqueOutput: path.join(baseDirs.output, TEST_RUN_ID),
77
57
  uniqueFixturesOutput: path.join(baseDirs.fixturesOutput, TEST_RUN_ID)
78
58
  };
79
-
80
- // Create the run-specific directories.
81
59
  await Promise.all([
82
60
  fs.mkdir(runDirs.uniqueOutput, { recursive: true }),
83
61
  fs.mkdir(runDirs.uniqueFixturesOutput, { recursive: true })
84
62
  ]);
63
+ originalSetupLog('[SETUP] RunDirs created.');
85
64
 
86
- // --- Set up global variables accessible within tests ---
87
- // This provides a consistent way for tests to access temporary paths.
88
- global.__TEST_DIRECTORIES__ = {
89
- ...baseDirs,
90
- ...runDirs
91
- };
92
-
93
- // Helper function for tests to get a path within the unique test output directory.
94
- global.getTestFilePath = (relPath) =>
95
- path.join(global.__TEST_DIRECTORIES__.uniqueOutput, relPath);
96
-
97
- // Helper function for tests to get a path within the unique fixture output directory.
98
- global.getTestFixturePath = (relPath) =>
99
- path.join(global.__TEST_DIRECTORIES__.uniqueFixturesOutput, relPath);
65
+ originalSetupLog('[SETUP] Setting globals...');
66
+ global.__TEST_DIRECTORIES__ = { ...baseDirs, ...runDirs };
67
+ global.getTestFilePath = (relPath) => path.join(global.__TEST_DIRECTORIES__.uniqueOutput, relPath);
68
+ global.getTestFixturePath = (relPath) => path.join(global.__TEST_DIRECTORIES__.uniqueFixturesOutput, relPath);
69
+ originalSetupLog('[SETUP] Globals set.');
100
70
 
101
- // --- Create specific mock files needed by certain tests ---
102
- // Example: Creating virtual font files for font processing tests.
103
- await ensureDir(path.dirname(path.join(tempDir, 'font.woff2'))); // Ensure parent dir exists
71
+ originalSetupLog('[SETUP] Ensuring mock font dirs...');
72
+ if (!tempDir) throw new Error("tempDir is not defined before font dir creation!");
73
+ await ensureDir(path.dirname(path.join(tempDir, 'font.woff2')));
104
74
  await ensureDir(path.dirname(path.join(tempDir, 'font.ttf')));
105
- // No need to ensureDir for 'missing.ttf' as we only need the path, not the file
75
+ originalSetupLog('[SETUP] Mock font dirs ensured.');
106
76
 
107
- // Write mock content to the font files.
77
+ originalSetupLog('[SETUP] Writing mock font files...');
108
78
  await fs.writeFile(path.join(tempDir, 'font.woff2'), 'mock woff2 data');
109
79
  await fs.writeFile(path.join(tempDir, 'font.ttf'), 'mock ttf data');
110
-
111
- // Set up a global variable pointing to the temp directory for potential use in mocks.
112
80
  global.__MOCK_FILE_PATH__ = tempDir;
81
+ originalSetupLog('[SETUP] Mock font files written.');
113
82
 
114
- // --- Create the stub/mock sample HTML project ---
115
- // **FIX APPLIED HERE:** Ensure directory exists *before* writing files into it
116
- // to prevent ENOENT race conditions.
117
-
118
- console.log(`Ensuring sample project directory exists: ${baseDirs.sampleProject}`);
119
- // 1. Ensure the sample project directory exists FIRST
83
+ originalSetupLog('[SETUP] Ensuring sample project dir...');
84
+ if (!baseDirs || !baseDirs.sampleProject) throw new Error("baseDirs.sampleProject is not defined!");
120
85
  await ensureDir(baseDirs.sampleProject);
86
+ originalSetupLog('[SETUP] Sample project dir ensured.');
121
87
 
122
- // 2. THEN write all the files into it using Promise.all for concurrency (safe now).
88
+ originalSetupLog('[SETUP] Writing sample project files...');
123
89
  try {
124
- console.log(`Writing sample project files to ${baseDirs.sampleProject}...`);
125
90
  await Promise.all([
126
- fs.writeFile(
127
- path.join(baseDirs.sampleProject, 'index.html'),
128
- `<!DOCTYPE html><html><head><link rel="stylesheet" href="styles.css"></head><body><img src="logo.png"/><script src="script.js"></script></body></html>`
129
- ),
130
- fs.writeFile(path.join(baseDirs.sampleProject, 'styles.css'), 'body { margin: 0; }'),
131
- fs.writeFile(path.join(baseDirs.sampleProject, 'script.js'), `console.log('hello');`),
132
- fs.writeFile(path.join(baseDirs.sampleProject, 'logo.png'), 'fake image data') // Using string for simplicity
91
+ fs.writeFile(path.join(baseDirs.sampleProject, 'index.html'), ``),
92
+ fs.writeFile(path.join(baseDirs.sampleProject, 'styles.css'), '/* Mock CSS */'),
93
+ fs.writeFile(path.join(baseDirs.sampleProject, 'script.js'), '// Mock JS'),
94
+ fs.writeFile(path.join(baseDirs.sampleProject, 'logo.png'), 'fake image data')
133
95
  ]);
134
- console.log(`Successfully wrote sample project files.`);
96
+ originalSetupLog('[SETUP] Sample project files written.');
135
97
  } catch (writeError) {
136
- console.error(`Failed to write sample project files to ${baseDirs.sampleProject}:`, writeError);
137
- // Depending on setup needs, you might want to stop the tests here.
138
- throw writeError; // Re-throw to potentially fail the setup.
98
+ originalSetupLog('[SETUP-ERROR] Failed to write sample project files:', writeError);
99
+ throw writeError;
139
100
  }
140
- // --- End Fix ---
141
101
 
142
- // --- Mock console methods ---
143
- // This helps keep test output clean and allows assertions on console messages.
102
+ originalSetupLog('[SETUP] Mocking console...');
144
103
  const originalLog = console.log;
145
104
  const originalWarn = console.warn;
146
105
  const originalError = console.error;
106
+ // Use the GLOBAL `jest` object to access `fn`
107
+ global.console.log = jest.fn((...args) => { if (process.env.DEBUG) originalLog(...args); });
108
+ global.console.warn = jest.fn((...args) => { /* ... filtering logic ... */ if (process.env.DEBUG) originalWarn(...args); });
109
+ global.console.error = jest.fn((...args) => { if (process.env.DEBUG) originalError(...args); });
110
+ originalSetupLog('[SETUP] Console mocked.');
147
111
 
148
- // Replace global console methods with Jest mocks.
149
- global.console.log = jest.fn((...args) => {
150
- // Only log to actual console if DEBUG environment variable is set.
151
- if (process.env.DEBUG) {
152
- originalLog(...args);
153
- }
154
- });
155
-
156
- global.console.warn = jest.fn((...args) => {
157
- // Optionally filter specific warnings you expect and don't want cluttering output.
158
- const msg = args.join(' ');
159
- if (
160
- msg.includes('Could not fetch asset') ||
161
- msg.includes('Error minifying')
162
- // Add other expected warning patterns here if needed
163
- ) {
164
- return; // Suppress specific known warnings
165
- }
166
- // Log other warnings, potentially only in debug mode.
167
- if (process.env.DEBUG) {
168
- originalWarn(...args);
169
- }
170
- });
171
-
172
- global.console.error = jest.fn((...args) => {
173
- // Always show errors, especially in debug mode.
174
- // Could add filtering if there are known, non-critical errors to ignore.
175
- if (process.env.DEBUG) {
176
- originalError(...args);
177
- }
178
- // Potentially log even without DEBUG for visibility during tests
179
- // originalError(...args);
180
- });
181
-
182
- console.log("Test setup complete."); // Log setup finish
112
+ originalSetupLog("[SETUP] Test setup complete.");
183
113
  });
184
114
 
185
- /**
186
- * Jest's global afterAll hook. Runs once after all tests in a suite file have completed.
187
- * Cleans up the temporary directory created for the test run.
188
- */
189
115
  afterAll(async () => {
190
- console.log(`Cleaning up test environment in: ${tempDir}`); // Log cleanup start
191
- const dirs = global.__TEST_DIRECTORIES__; // Retrieve paths from global scope
192
- if (!dirs) {
193
- console.warn("Global test directories not found during cleanup.");
194
- return;
195
- }
196
-
116
+ originalSetupLog(`[SETUP] Cleaning up test environment in: ${tempDir}`);
197
117
  try {
198
- // Recursively remove the entire temporary directory for this test run.
199
- // Using force: true helps ignore errors if files are missing (e.g., cleanup already ran partially).
200
- await fs.rm(tempDir, { recursive: true, force: true });
201
- console.log(`Successfully cleaned up test directory: ${tempDir}`);
118
+ if (tempDir) {
119
+ await fs.rm(tempDir, { recursive: true, force: true });
120
+ originalSetupLog(`[SETUP] Successfully cleaned up: ${tempDir}`);
121
+ } else {
122
+ originalSetupLog(`[SETUP-WARN] tempDir variable was not defined during cleanup.`);
123
+ }
202
124
  } catch (e) {
203
- // Log errors during cleanup but don't fail the tests typically.
204
- // ENOENT means the directory was already gone, which is fine.
205
125
  if (e.code !== 'ENOENT') {
206
- console.warn(`Could not fully clean test directory ${tempDir}:`, e.message);
126
+ originalSetupLog(`[SETUP-WARN] Could not fully clean test directory ${tempDir}:`, e.message);
207
127
  } else {
208
- console.log(`Test directory already removed: ${tempDir}`);
128
+ originalSetupLog(`[SETUP] Test directory already removed: ${tempDir}`);
209
129
  }
210
130
  }
211
131
  });
package/package.json CHANGED
@@ -1,25 +1,25 @@
1
1
  {
2
2
  "name": "portapack",
3
- "version": "0.2.1",
3
+ "version": "0.3.1",
4
4
  "description": "📦 A tool to bundle and minify HTML and all its dependencies into a single portable file.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
8
8
  "type": "module",
9
9
  "bin": {
10
- "portapack": "./dist/cli/cli-entry.js"
10
+ "portapack": "./dist/cli/cli-entry.cjs"
11
11
  },
12
12
  "scripts": {
13
13
  "dev": "concurrently --success=all -n BUILD,DOCS,TEST -c green,blue,magenta \"npm run dev:build\" \"npm run dev:docs\" \"npm run dev:test\"",
14
14
  "dev:build": "tsup --watch",
15
15
  "dev:docs": "concurrently \"npm run docs:api:watch\" \"npm run docs:dev\"",
16
16
  "dev:test": "cross-env FORCE_COLOR=1 jest --watch --clearCache --passWithNoTests --watchPathIgnorePatterns=\"<rootDir>/tests/__fixtures__/output\"",
17
- "dev:test:debug": "cross-env NODE_OPTIONS=--experimental-vm-modules FORCE_COLOR=1 jest --watch --runTestsByPath",
17
+ "dev:test:debug": "cross-env FORCE_COLOR=1 jest --watch --runTestsByPath",
18
18
  "build": "npm run build:code && npm run docs:api && npm run docs:build",
19
19
  "build:code": "tsup",
20
20
  "example": "npx tsx examples/main.ts",
21
- "test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest --coverage",
22
- "test:ci": "cross-env NODE_OPTIONS=--experimental-vm-modules jest --coverage --ci",
21
+ "test": "cross-env jest --coverage",
22
+ "test:ci": "cross-env jest --coverage --ci",
23
23
  "test:clear": "jest --clearCache",
24
24
  "coverage": "jest --coverage && open coverage/lcov-report/index.html",
25
25
  "docs:coverage": "npm run test && mkdir -p docs/test-coverage && cp -r coverage/lcov-report/* docs/test-coverage/",
@@ -1,28 +1,28 @@
1
1
  /**
2
2
  * @file cli-entry.ts
3
3
  * @description
4
- * Node.js entry point for PortaPack CLI (compatible with ESM).
5
- *
6
- * Supports:
7
- * - Direct execution: `node cli-entry.js`
8
- * - Programmatic import for testing: `import { startCLI } from './cli-entry'`
4
+ * Safe Node.js CLI entrypoint for PortaPack, compatible with both ESM and CommonJS output.
9
5
  */
10
6
 
11
7
  import type { CLIResult } from '../types';
12
8
 
13
- /**
14
- * Starts the CLI by importing and invoking the main CLI logic.
15
- *
16
- * @returns {Promise<CLIResult>} - Exit code and any captured output
17
- */
18
9
  const startCLI = async (): Promise<CLIResult> => {
19
- const { main } = await import('./cli.js');
10
+ const { main } = await import('./cli.js'); // This stays ESM-friendly
20
11
  return await main(process.argv);
21
12
  };
22
13
 
23
- // If executed directly from the command line, run and exit.
24
- if (import.meta.url === `file://${process.argv[1]}`) {
25
- startCLI().then(({ exitCode }) => process.exit(Number(exitCode))); // Cast exitCode to Number
14
+ // Safe: if this file is the entry point, run the CLI
15
+ if (require.main === module) {
16
+ startCLI()
17
+ .then(({ stdout, stderr, exitCode }) => {
18
+ if (stdout) process.stdout.write(stdout);
19
+ if (stderr) process.stderr.write(stderr);
20
+ process.exit(Number(exitCode));
21
+ })
22
+ .catch((err) => {
23
+ console.error('💥 Unhandled CLI error:', err);
24
+ process.exit(1);
25
+ });
26
26
  }
27
27
 
28
- export { startCLI };
28
+ export { startCLI };