milkee 3.1.2 → 3.1.3-dev.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/README-ja.md CHANGED
@@ -5,6 +5,10 @@
5
5
  [![Downloads](https://img.shields.io/npm/dw/milkee.svg)](https://www.npmjs.com/package/milkee)
6
6
  [![License](https://img.shields.io/npm/l/milkee.svg)](./LICENSE)
7
7
 
8
+ <a href="https://www.buymeacoffee.com/devuloper" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 60px !important;width: 217px !important;" ></a>
9
+
10
+ <a href='https://ko-fi.com/E1E41LY2C9' target='_blank'><img height='36' style='border:0px;height:36px;' src='https://storage.ko-fi.com/cdn/kofi6.png?v=6' border='0' alt='Buy Me a Coffee at ko-fi.com' /></a>
11
+
8
12
  [English](./README.md) | **日本語**
9
13
 
10
14
  [![Milkee logo](./img/Milkee-logo.png)](https://milkee.org)
package/README.md CHANGED
@@ -5,6 +5,10 @@
5
5
  [![Downloads](https://img.shields.io/npm/dw/milkee.svg)](https://www.npmjs.com/package/milkee)
6
6
  [![License](https://img.shields.io/npm/l/milkee.svg)](./LICENSE)
7
7
 
8
+ <a href="https://www.buymeacoffee.com/devuloper" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 60px !important;width: 217px !important;" ></a>
9
+
10
+ <a href='https://ko-fi.com/E1E41LY2C9' target='_blank'><img height='36' style='border:0px;height:36px;' src='https://storage.ko-fi.com/cdn/kofi6.png?v=6' border='0' alt='Buy Me a Coffee at ko-fi.com' /></a>
11
+
8
12
  **English** | [日本語](./README-ja.md)
9
13
 
10
14
  [![Milkee logo](./img/Milkee-logo.png)](https://milkee.org)
@@ -28,7 +28,7 @@ compile = async function() {
28
28
  var action, backupFiles, cl, compilerProcess, config, debounceTimeout, enabledOptions, enabledOptionsList, error, execCommand, execCommandParts, execOtherOptionStrings, installCmd, lastError, milkee, milkeeOptions, options, spawnArgs, summary, toContinue;
29
29
  cl = (await checkLatest());
30
30
  if (cl) {
31
- action = (await consola.prompt("Do you want to update now?", {
31
+ action = (await consola.prompt('Do you want to update now?', {
32
32
  type: 'select',
33
33
  options: [
34
34
  {
@@ -50,7 +50,7 @@ compile = async function() {
50
50
  }));
51
51
  if (action && action !== 'skip') {
52
52
  installCmd = action === 'global' ? 'npm i -g milkee@latest' : 'npm i -D milkee@latest';
53
- consola.start("Updating milkee...");
53
+ consola.start('Updating milkee...');
54
54
  await new Promise(function(resolve) {
55
55
  var cp;
56
56
  cp = spawn(installCmd, {
@@ -59,10 +59,10 @@ compile = async function() {
59
59
  });
60
60
  return cp.on('close', resolve);
61
61
  });
62
- consola.success("Update finished! Please run the command again.");
62
+ consola.success('Update finished! Please run the command again.');
63
63
  process.exit(0);
64
64
  } else if (action === 'skip') {
65
- consola.info("Skipped!");
65
+ consola.info('Skipped!');
66
66
  } else if (!action) {
67
67
  process.exit(1);
68
68
  }
@@ -157,7 +157,7 @@ compile = async function() {
157
157
  summary.push(`Options: ${enabledOptionsList}`);
158
158
  }
159
159
  consola.box({
160
- title: "Milkee Compilation Summary",
160
+ title: 'Milkee Compilation Summary',
161
161
  message: summary.join('\n')
162
162
  });
163
163
  if (milkeeOptions.confirm) {
@@ -181,7 +181,7 @@ compile = async function() {
181
181
  consola.start(`Watching for changes in \`${config.entry}\`...`);
182
182
  consola.info(`Executing: coffee ${spawnArgs.join(' ')}`);
183
183
  if (milkeeOptions.refresh) {
184
- consola.warn("Refresh backup is disabled in watch mode (backups are cleared immediately).");
184
+ consola.warn('Refresh backup is disabled in watch mode (backups are cleared immediately).');
185
185
  clearBackups(backupFiles);
186
186
  }
187
187
  compilerProcess = spawn('coffee', spawnArgs, {
@@ -210,7 +210,7 @@ compile = async function() {
210
210
  }
211
211
  return debounceTimeout = setTimeout(function() {
212
212
  if (lastError) {
213
- consola.warn("Compilation failed, plugins skipped.");
213
+ consola.warn('Compilation failed, plugins skipped.');
214
214
  } else {
215
215
  consola.success('Compilation successful (watch mode).');
216
216
  runPlugins(config, {...(config.options || {})}, '(watch mode)', '');
@@ -128,11 +128,11 @@ updatePackageJson = function() {
128
128
  }
129
129
  }
130
130
  fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
131
- consola.success("Updated `package.json`");
131
+ consola.success('Updated `package.json`');
132
132
  return true;
133
133
  } catch (error1) {
134
134
  error = error1;
135
- consola.error("Failed to update package.json:", error);
135
+ consola.error('Failed to update package.json:', error);
136
136
  return false;
137
137
  }
138
138
  };
@@ -142,16 +142,16 @@ initPackageJson = function() {
142
142
  var error, pkgPath;
143
143
  pkgPath = path.join(CWD, 'package.json');
144
144
  if (!fs.existsSync(pkgPath)) {
145
- consola.start("Initializing package.json...");
145
+ consola.start('Initializing package.json...');
146
146
  try {
147
147
  execSync('npm init', {
148
148
  cwd: CWD,
149
149
  stdio: 'inherit'
150
150
  });
151
- consola.success("Created `package.json`");
151
+ consola.success('Created `package.json`');
152
152
  } catch (error1) {
153
153
  error = error1;
154
- consola.error("Failed to create package.json:", error);
154
+ consola.error('Failed to create package.json:', error);
155
155
  return false;
156
156
  }
157
157
  }
@@ -176,11 +176,11 @@ generateReadme = function() {
176
176
  readme = readme.replace(/\{\{name\}\}/g, name);
177
177
  readme = readme.replace(/\{\{description\}\}/g, description);
178
178
  fs.writeFileSync(readmePath, readme);
179
- consola.success("Created `README.md`");
179
+ consola.success('Created `README.md`');
180
180
  return true;
181
181
  } catch (error1) {
182
182
  error = error1;
183
- consola.error("Failed to create README.md:", error);
183
+ consola.error('Failed to create README.md:', error);
184
184
  return false;
185
185
  }
186
186
  };
@@ -188,41 +188,41 @@ generateReadme = function() {
188
188
  // Main plugin setup function
189
189
  plugin = async function() {
190
190
  var confirmed, destPath, doc, error, i, j, k, l, len, len1, len2, len3, overwrite, pkgPath, readmePath, template;
191
- consola.box("Milkee Plugin Setup");
192
- consola.info("This will set up your project as a Milkee plugin.");
193
- consola.info("");
194
- consola.info("The following actions will be performed:");
191
+ consola.box('Milkee Plugin Setup');
192
+ consola.info('This will set up your project as a Milkee plugin.');
193
+ consola.info('');
194
+ consola.info('The following actions will be performed:');
195
195
  pkgPath = path.join(CWD, 'package.json');
196
196
  if (!fs.existsSync(pkgPath)) {
197
- consola.info(" 0. Initialize package.json (npm init)");
197
+ consola.info(' 0. Initialize package.json (npm init)');
198
198
  }
199
- consola.info(" 1. Install dependencies (consola, coffeescript, milkee)");
200
- consola.info(" 2. Create template files:");
199
+ consola.info(' 1. Install dependencies (consola, coffeescript, milkee)');
200
+ consola.info(' 2. Create template files:');
201
201
  for (i = 0, len = TEMPLATES.length; i < len; i++) {
202
202
  template = TEMPLATES[i];
203
203
  consola.info(` - ${template.dest}`);
204
204
  }
205
- consola.info(" 3. Copy docs:");
205
+ consola.info(' 3. Copy docs:');
206
206
  for (j = 0, len1 = DOCS.length; j < len1; j++) {
207
207
  doc = DOCS[j];
208
208
  consola.info(` - ${doc.dest}`);
209
209
  }
210
- consola.info(" 4. Update package.json (main, scripts, keywords)");
211
- consola.info(" 5. Generate README.md");
212
- consola.info("");
210
+ consola.info(' 4. Update package.json (main, scripts, keywords)');
211
+ consola.info(' 5. Generate README.md');
212
+ consola.info('');
213
213
  // Confirm before proceeding
214
214
  confirmed = (await confirmContinue());
215
215
  if (!confirmed) {
216
216
  return;
217
217
  }
218
- consola.info("");
218
+ consola.info('');
219
219
  // Initialize package.json if not exists
220
220
  if (!initPackageJson()) {
221
221
  return;
222
222
  }
223
223
  try {
224
224
  // Install dependencies
225
- consola.start("Installing dependencies...");
225
+ consola.start('Installing dependencies...');
226
226
  execSync('npm install consola', {
227
227
  cwd: CWD,
228
228
  stdio: 'inherit'
@@ -231,21 +231,21 @@ plugin = async function() {
231
231
  cwd: CWD,
232
232
  stdio: 'inherit'
233
233
  });
234
- consola.success("Dependencies installed!");
234
+ consola.success('Dependencies installed!');
235
235
  } catch (error1) {
236
236
  error = error1;
237
- consola.error("Failed to install dependencies:", error);
237
+ consola.error('Failed to install dependencies:', error);
238
238
  return;
239
239
  }
240
- consola.info("");
240
+ consola.info('');
241
241
  // Create template files
242
- consola.start("Creating template files...");
242
+ consola.start('Creating template files...');
243
243
  for (k = 0, len2 = TEMPLATES.length; k < len2; k++) {
244
244
  template = TEMPLATES[k];
245
245
  destPath = path.join(CWD, template.dest);
246
246
  if (fs.existsSync(destPath)) {
247
247
  overwrite = (await consola.prompt(`${template.dest} already exists. Overwrite?`, {
248
- type: "confirm"
248
+ type: 'confirm'
249
249
  }));
250
250
  if (!overwrite) {
251
251
  consola.info(`Skipped \`${template.dest}\``);
@@ -254,15 +254,15 @@ plugin = async function() {
254
254
  }
255
255
  copyTemplate(template.src, template.dest);
256
256
  }
257
- consola.info("");
257
+ consola.info('');
258
258
  // Copy docs
259
- consola.start("Copying docs...");
259
+ consola.start('Copying docs...');
260
260
  for (l = 0, len3 = DOCS.length; l < len3; l++) {
261
261
  doc = DOCS[l];
262
262
  destPath = path.join(CWD, doc.dest);
263
263
  if (fs.existsSync(destPath)) {
264
264
  overwrite = (await consola.prompt(`${doc.dest} already exists. Overwrite?`, {
265
- type: "confirm"
265
+ type: 'confirm'
266
266
  }));
267
267
  if (!overwrite) {
268
268
  consola.info(`Skipped \`${doc.dest}\``);
@@ -271,32 +271,32 @@ plugin = async function() {
271
271
  }
272
272
  copyDocs(doc.src, doc.dest);
273
273
  }
274
- consola.info("");
274
+ consola.info('');
275
275
  // Update package.json
276
- consola.start("Updating package.json...");
276
+ consola.start('Updating package.json...');
277
277
  updatePackageJson();
278
- consola.info("");
278
+ consola.info('');
279
279
  // Generate README.md
280
280
  readmePath = path.join(CWD, 'README.md');
281
281
  if (fs.existsSync(readmePath)) {
282
- overwrite = (await consola.prompt("README.md already exists. Overwrite?", {
283
- type: "confirm"
282
+ overwrite = (await consola.prompt('README.md already exists. Overwrite?', {
283
+ type: 'confirm'
284
284
  }));
285
285
  if (overwrite) {
286
286
  generateReadme();
287
287
  } else {
288
- consola.info("Skipped `README.md`");
288
+ consola.info('Skipped `README.md`');
289
289
  }
290
290
  } else {
291
291
  generateReadme();
292
292
  }
293
- consola.info("");
294
- consola.success("Milkee plugin setup complete!");
295
- consola.info("");
296
- consola.info("Next steps:");
297
- consola.info(" 1. Edit `src/main.coffee` to implement your plugin");
298
- consola.info(" 2. Run `npm run build` to compile");
299
- return consola.info(" 3. Test your plugin locally");
293
+ consola.info('');
294
+ consola.success('Milkee plugin setup complete!');
295
+ consola.info('');
296
+ consola.info('Next steps:');
297
+ consola.info(' 1. Edit `src/main.coffee` to implement your plugin');
298
+ consola.info(' 2. Run `npm run build` to compile');
299
+ return consola.info(' 3. Test your plugin locally');
300
300
  };
301
301
 
302
302
  module.exports = plugin;
@@ -15,23 +15,23 @@ consola = require('consola');
15
15
  setup = async function() {
16
16
  var CONFIG_TEMPLATE, TEMPLATE_PATH, check, error, pstat, stat;
17
17
  checkCoffee();
18
- pstat = "created";
19
- stat = "create";
18
+ pstat = 'created';
19
+ stat = 'create';
20
20
  if (fs.existsSync(CONFIG_PATH)) {
21
21
  consola.warn(`\`${CONFIG_FILE}\` already exists in this directory.`);
22
22
  check = (await consola.prompt(`Do you want to reset \`${CONFIG_FILE}\`?`, {
23
- type: "confirm"
23
+ type: 'confirm'
24
24
  }));
25
25
  if (!check) {
26
- consola.info("Cancelled.");
26
+ consola.info('Cancelled.');
27
27
  return;
28
28
  } else {
29
29
  fs.rmSync(CONFIG_PATH, {
30
30
  recursive: true,
31
31
  force: true
32
32
  });
33
- pstat = "reset";
34
- stat = "reset";
33
+ pstat = 'reset';
34
+ stat = 'reset';
35
35
  }
36
36
  }
37
37
  try {
@@ -20,7 +20,7 @@ checkLatest = async function() {
20
20
  res = (await isPackageLatest(pkg));
21
21
  if (res.success && !res.isLatest) {
22
22
  consola.box({
23
- title: "A new version is now available!",
23
+ title: 'A new version is now available!',
24
24
  message: `${res.currentVersion} --> \`${res.latestVersion}\`\n\n# global installation\n\`npm i -g milkee@latest\`\n# or local installation\n\`npm i -D milkee@latest\``
25
25
  });
26
26
  return true;
@@ -29,10 +29,10 @@ executePlugins = function(config, compilationResult) {
29
29
  consola.warn(`Invalid plugin definition skipped (expected a function, got ${typeof pluginFn}).`);
30
30
  }
31
31
  }
32
- return consola.success("Plugins executed successfully.");
32
+ return consola.success('Plugins executed successfully.');
33
33
  } catch (error1) {
34
34
  error = error1;
35
- return consola.error("An error occurred during plugin execution:", error);
35
+ return consola.error('An error occurred during plugin execution:', error);
36
36
  }
37
37
  })();
38
38
  };
package/dist/main.js CHANGED
@@ -15,15 +15,15 @@ plugin = require('./commands/plugin');
15
15
 
16
16
  argv = yargs(hideBin(process.argv)).scriptName('milkee').usage('$0 [command]').option('setup', {
17
17
  alias: 's',
18
- describe: "Generate a default config file",
18
+ describe: 'Generate a default config file',
19
19
  type: 'boolean'
20
20
  }).option('compile', {
21
21
  alias: 'c',
22
- describe: "Compile CoffeeScript (default)",
22
+ describe: 'Compile CoffeeScript (default)',
23
23
  type: 'boolean'
24
24
  }).option('plugin', {
25
25
  alias: 'p',
26
- describe: "Set up a new Milkee plugin project",
26
+ describe: 'Set up a new Milkee plugin project',
27
27
  type: 'boolean'
28
28
  }).version('version', pkg.version).alias('v', 'version').help('help').alias('h', 'help').argv;
29
29
 
@@ -6,11 +6,11 @@ consola = require('consola');
6
6
  // Confirm processing
7
7
  confirmContinue = async function() {
8
8
  var toContinue;
9
- toContinue = (await consola.prompt("Do you want to continue?", {
10
- type: "confirm"
9
+ toContinue = (await consola.prompt('Do you want to continue?', {
10
+ type: 'confirm'
11
11
  }));
12
12
  if (!toContinue) {
13
- consola.info("Canceled.");
13
+ consola.info('Canceled.');
14
14
  return false;
15
15
  }
16
16
  return true;
@@ -18,7 +18,7 @@ executeCopy = function(config) {
18
18
  return;
19
19
  }
20
20
  if ((ref = config.options) != null ? ref.join : void 0) {
21
- consola.info("Copy skipped (join option is enabled)");
21
+ consola.info('Copy skipped (join option is enabled)');
22
22
  return;
23
23
  }
24
24
  if (!fs.existsSync(outputPath)) {
@@ -27,10 +27,10 @@ executeCopy = function(config) {
27
27
  }
28
28
  stat = fs.statSync(entryPath);
29
29
  if (!stat.isDirectory()) {
30
- consola.info("Copy skipped (entry is not a directory)");
30
+ consola.info('Copy skipped (entry is not a directory)');
31
31
  return;
32
32
  }
33
- consola.start("Copying non-coffee files...");
33
+ consola.start('Copying non-coffee files...');
34
34
  try {
35
35
  copyNonCoffeeFiles = function(srcDir, destDir) {
36
36
  var destItemPath, i, item, items, len, parentDir, results, srcItemPath;
@@ -68,10 +68,10 @@ executeCopy = function(config) {
68
68
  return results;
69
69
  };
70
70
  copyNonCoffeeFiles(entryPath, outputPath);
71
- return consola.success("Non-coffee files copied successfully!");
71
+ return consola.success('Non-coffee files copied successfully!');
72
72
  } catch (error1) {
73
73
  error = error1;
74
- consola.error("Failed to copy non-coffee files:", error);
74
+ consola.error('Failed to copy non-coffee files:', error);
75
75
  throw error;
76
76
  }
77
77
  };
@@ -20,9 +20,9 @@ executeRefresh = function(config, backupFiles) {
20
20
  hash = crypto.randomBytes(4).toString('hex');
21
21
  try {
22
22
  if (stat.isDirectory()) {
23
- consola.info("Executing: Refresh");
23
+ consola.info('Executing: Refresh');
24
24
  items = fs.readdirSync(targetDir);
25
- consola.start("Backing up files...");
25
+ consola.start('Backing up files...');
26
26
  for (i = 0, len = items.length; i < len; i++) {
27
27
  item = items[i];
28
28
  originalPath = path.join(targetDir, item);
@@ -36,7 +36,7 @@ executeRefresh = function(config, backupFiles) {
36
36
  }
37
37
  return consola.success(`Files backed up with hash \`${hash}\``);
38
38
  } else {
39
- consola.info("Executing: Refresh (Single File)");
39
+ consola.info('Executing: Refresh (Single File)');
40
40
  originalPath = targetDir;
41
41
  fileName = path.basename(originalPath);
42
42
  dirName = path.dirname(originalPath);
@@ -51,18 +51,18 @@ executeRefresh = function(config, backupFiles) {
51
51
  }
52
52
  } catch (error1) {
53
53
  error = error1;
54
- consola.error("Failed to create backups during refresh:", error);
54
+ consola.error('Failed to create backups during refresh:', error);
55
55
  throw error;
56
56
  }
57
57
  } else {
58
- return consola.info("Refresh skipped.");
58
+ return consola.info('Refresh skipped.');
59
59
  }
60
60
  };
61
61
 
62
62
  // Restore backup files
63
63
  restoreBackups = function(backupFiles) {
64
64
  var backup, e, i, len;
65
- consola.info("Restoring previous files...");
65
+ consola.info('Restoring previous files...');
66
66
  if (backupFiles.length > 0) {
67
67
  for (i = 0, len = backupFiles.length; i < len; i++) {
68
68
  backup = backupFiles[i];
@@ -80,9 +80,9 @@ restoreBackups = function(backupFiles) {
80
80
  consola.warn(`Failed to restore ${backup.original}`);
81
81
  }
82
82
  }
83
- return consola.success("Restored!");
83
+ return consola.success('Restored!');
84
84
  } else {
85
- return consola.info("No files found to restore.");
85
+ return consola.info('No files found to restore.');
86
86
  }
87
87
  };
88
88
 
@@ -90,7 +90,7 @@ restoreBackups = function(backupFiles) {
90
90
  clearBackups = function(backupFiles) {
91
91
  var backup, e, i, len, results;
92
92
  if (backupFiles.length > 0) {
93
- consola.start("Cleaning up backups...");
93
+ consola.start('Cleaning up backups...');
94
94
  results = [];
95
95
  for (i = 0, len = backupFiles.length; i < len; i++) {
96
96
  backup = backupFiles[i];
package/docs/PLUGIN-ja.md CHANGED
@@ -131,7 +131,7 @@ npm publish --access public
131
131
  ### 命名規則 & キーワード
132
132
 
133
133
  - 名前: `milkee-plugin-xxx` または `@yourname/milkee-plugin-xxx`
134
- - キーワード(自動追加): `milkee`, `coffeescript`, `coffee`, `ext`, `plugin`, `milkee-plugin`
134
+ - キーワード(自動追加): `milkee`, `coffeescript`, `coffee`, `plugin`, `milkee-plugin`
135
135
 
136
136
  ### エラーハンドリング
137
137
 
package/docs/PLUGIN.md CHANGED
@@ -131,7 +131,7 @@ npm publish --access public
131
131
  ### Naming & Keywords
132
132
 
133
133
  - Name: `milkee-plugin-xxx` or `@yourname/milkee-plugin-xxx`
134
- - Keywords (auto-added): `milkee`, `coffeescript`, `coffee`, `ext`, `plugin`, `milkee-plugin`
134
+ - Keywords (auto-added): `milkee`, `coffeescript`, `coffee`, `plugin`, `milkee-plugin`
135
135
 
136
136
  ### Error Handling
137
137
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "milkee",
3
- "version": "3.1.2",
3
+ "version": "3.1.3-dev.0",
4
4
  "description": "A simple CoffeeScript build tool with coffee.config.cjs",
5
5
  "main": "dist/main.js",
6
6
  "bin": {
@@ -12,7 +12,7 @@
12
12
  "coverage": "vitest --run --coverage",
13
13
  "checkversion": "ncu",
14
14
  "build": "coffee --output dist/ --compile --bare src/",
15
- "format": "node ./scripts/format.js",
15
+ "format": "cprettier ./src/",
16
16
  "deprecate:dev": "node ./scripts/deprecate-dev-versions.js"
17
17
  },
18
18
  "repository": {
@@ -50,8 +50,8 @@
50
50
  "coffeescript": "^2.7.0",
51
51
  "dotenv": "^17.2.3",
52
52
  "execa": "^9.6.1",
53
+ "milkee-plugin-prettier": "^1.0.0",
53
54
  "npm-check-updates": "^19.3.1",
54
- "prettier": "^3.8.0",
55
55
  "vitest": "^4.0.17"
56
56
  }
57
57
  }
package/.prettierrc.json DELETED
@@ -1,3 +0,0 @@
1
- {
2
- "tabWidth": 2
3
- }
@@ -1,36 +0,0 @@
1
- const fs = require("fs");
2
- const path = require("path");
3
- const os = require("os");
4
- const { execFile } = require("child_process");
5
-
6
- describe("cli setup integration", () => {
7
- it("runs `--setup` and creates coffee.config.cjs", async () => {
8
- const dir = createTestDir("cli");
9
-
10
- // run the setup command directly via CoffeeScript so we don't require the ESM-only `yargs` in `main`
11
- const scriptPath = require("path").join(
12
- process.cwd(),
13
- "src",
14
- "commands",
15
- "setup.coffee",
16
- );
17
- const csRegister = require.resolve("coffeescript/register");
18
- await new Promise((resolve, reject) => {
19
- // -r <abs> -> preload CoffeeScript via absolute path so child process can find it
20
- execFile(
21
- "node",
22
- ["-r", csRegister, "-e", `require(${JSON.stringify(scriptPath)})()`],
23
- { cwd: dir },
24
- (err) => {
25
- if (err) return reject(err);
26
- resolve();
27
- },
28
- );
29
- });
30
-
31
- const cfgPath = path.join(dir, "coffee.config.cjs");
32
- expect(fs.existsSync(cfgPath)).toBe(true);
33
-
34
- fs.rmSync(dir, { recursive: true, force: true });
35
- });
36
- });
package/test/setup.js DELETED
@@ -1,113 +0,0 @@
1
- // Register CoffeeScript so tests can require .coffee files
2
- require("coffeescript/register");
3
-
4
- // Test sandbox guard: prevent accidental writes outside a temporary sandbox
5
- const fs = require("fs");
6
- const path = require("path");
7
- const os = require("os");
8
-
9
- const SANDBOX = fs.mkdtempSync(path.join(os.tmpdir(), "milkee-test-sandbox-"));
10
- process.env.TEST_SANDBOX = SANDBOX;
11
- globalThis.TEST_SANDBOX = SANDBOX;
12
-
13
- function stripExtendedPrefix(s) {
14
- if (typeof s !== "string") return s;
15
- // Remove Windows extended path prefix like '\\?\\' or '\?\\'
16
- let out = s.replace(/^\\+\?\\/, "");
17
- // If we still have a leading backslash before a drive letter (e.g. '\C:\...'), remove it
18
- out = out.replace(/^\\+([A-Za-z]:)/, "$1");
19
- return out;
20
- }
21
-
22
- // Allowlist: sandbox + system temp + node_modules + any additional paths via TEST_WRITE_ALLOW
23
- const allowed = [
24
- path.resolve(stripExtendedPrefix(SANDBOX)),
25
- path.resolve(stripExtendedPrefix(os.tmpdir())),
26
- path.resolve(stripExtendedPrefix(process.cwd()), "node_modules"),
27
- ];
28
- if (process.env.TEST_WRITE_ALLOW) {
29
- process.env.TEST_WRITE_ALLOW.split(",").forEach((p) => {
30
- if (p) allowed.push(path.resolve(stripExtendedPrefix(p)));
31
- });
32
- }
33
-
34
- function resolveSafe(p) {
35
- if (!p) return p;
36
- // accept Buffers as well
37
- if (Buffer.isBuffer(p)) p = p.toString();
38
- // strip Windows extended path prefix if present
39
- if (typeof p === "string") {
40
- p = stripExtendedPrefix(p);
41
- }
42
- if (!path.isAbsolute(p)) p = path.join(process.cwd(), String(p));
43
- return path.resolve(p);
44
- }
45
-
46
- function isAllowed(p) {
47
- const rp = resolveSafe(p);
48
- return allowed.some(
49
- (prefix) => rp === prefix || rp.startsWith(prefix + path.sep),
50
- );
51
- }
52
-
53
- function makeGuard(orig, checkIndex = 0) {
54
- return function (...args) {
55
- const target = args[checkIndex];
56
- if (!isAllowed(target)) {
57
- throw new Error(
58
- `Test attempted to modify outside sandbox: ${target} (sandbox=${SANDBOX})`,
59
- );
60
- }
61
- return orig.apply(this, args);
62
- };
63
- }
64
-
65
- // Patch common fs methods that write or modify the FS
66
- const writeSyncMethods = [
67
- "writeFileSync",
68
- "appendFileSync",
69
- "copyFileSync",
70
- "mkdirSync",
71
- "rmSync",
72
- "rmdirSync",
73
- "renameSync",
74
- "unlinkSync",
75
- ];
76
- for (const m of writeSyncMethods) {
77
- if (typeof fs[m] === "function") fs[m] = makeGuard(fs[m], 0);
78
- }
79
-
80
- // Async versions
81
- if (typeof fs.writeFile === "function") {
82
- const orig = fs.writeFile;
83
- fs.writeFile = function (file, ...rest) {
84
- if (!isAllowed(file)) {
85
- const cb = rest.find((r) => typeof r === "function");
86
- const err = new Error(
87
- `Test attempted to modify outside sandbox: ${file} (sandbox=${SANDBOX})`,
88
- );
89
- if (cb) return process.nextTick(() => cb(err));
90
- throw err;
91
- }
92
- return orig.apply(this, [file, ...rest]);
93
- };
94
- }
95
- if (fs.promises && typeof fs.promises.writeFile === "function") {
96
- const orig = fs.promises.writeFile;
97
- fs.promises.writeFile = function (file, ...rest) {
98
- if (!isAllowed(file))
99
- throw new Error(
100
- `Test attempted to modify outside sandbox: ${file} (sandbox=${SANDBOX})`,
101
- );
102
- return orig.apply(this, [file, ...rest]);
103
- };
104
- }
105
-
106
- // Helper for tests to create safe temp dirs
107
- globalThis.createTestDir = function (name = "") {
108
- const dir = path.join(SANDBOX, name || String(Date.now()));
109
- fs.mkdirSync(dir, { recursive: true });
110
- return dir;
111
- };
112
-
113
- console.info(`[test setup] test sandbox: ${SANDBOX}`);
@@ -1,38 +0,0 @@
1
- const fs = require("fs");
2
- const path = require("path");
3
- const consola = require("consola");
4
-
5
- // We re-require the checks module inside tests to allow us to mock dependencies
6
- // that it captures at require-time.
7
-
8
- describe("checks", () => {
9
- afterEach(() => {
10
- vi.restoreAllMocks();
11
- vi.resetModules();
12
- });
13
-
14
- it("checkLatest returns true and shows box when not latest", async () => {
15
- vi.mock("is-package-latest", () => ({
16
- isPackageLatest: async () => ({
17
- success: true,
18
- isLatest: false,
19
- currentVersion: "1.0.0",
20
- latestVersion: "1.2.0",
21
- }),
22
- }));
23
- const { checkLatest } = require("../../src/lib/checks.coffee");
24
-
25
- vi.spyOn(consola, "box").mockImplementation(() => {});
26
-
27
- const res = await checkLatest();
28
- expect(res).toBe(true);
29
- expect(consola.box).toHaveBeenCalled();
30
- });
31
-
32
- it("checkCoffee reads package.json and does not warn when coffeescript present", async () => {
33
- const { checkCoffee } = require("../../src/lib/checks.coffee");
34
- vi.spyOn(consola, "warn").mockImplementation(() => {});
35
- await checkCoffee();
36
- expect(consola.warn).not.toHaveBeenCalled();
37
- });
38
- });
@@ -1,50 +0,0 @@
1
- const fs = require("fs");
2
- const path = require("path");
3
- const os = require("os");
4
-
5
- const executeCopy = require("../../src/options/copy.coffee");
6
-
7
- function createTree(base) {
8
- fs.mkdirSync(base, { recursive: true });
9
- fs.writeFileSync(path.join(base, "a.coffee"), "c");
10
- fs.writeFileSync(path.join(base, "b.txt"), "b");
11
- fs.mkdirSync(path.join(base, "sub"), { recursive: true });
12
- fs.writeFileSync(path.join(base, "sub", "c.md"), "c");
13
- }
14
-
15
- describe("copy", () => {
16
- it("copies non-coffee files recursively", () => {
17
- const base = createTestDir("copy");
18
- const entry = path.join(base, "entry");
19
- const out = path.join(base, "out");
20
- createTree(entry);
21
- fs.mkdirSync(out, { recursive: true });
22
-
23
- // preconditions
24
- expect(fs.existsSync(entry)).toBe(true);
25
- expect(fs.existsSync(path.join(entry, "b.txt"))).toBe(true);
26
-
27
- // use absolute paths (sandboxed)
28
- executeCopy({ entry, output: out });
29
-
30
- expect(fs.existsSync(path.join(out, "b.txt"))).toBe(true);
31
- expect(fs.existsSync(path.join(out, "sub", "c.md"))).toBe(true);
32
- expect(fs.existsSync(path.join(out, "a.coffee"))).toBe(false);
33
-
34
- fs.rmSync(base, { recursive: true, force: true });
35
- });
36
-
37
- it("skips when join option is enabled", () => {
38
- const base = createTestDir("copy");
39
- const entry = path.join(base, "entry");
40
- const out = path.join(base, "out");
41
- createTree(entry);
42
- fs.mkdirSync(out, { recursive: true });
43
-
44
- executeCopy({ entry, output: out, options: { join: true } });
45
-
46
- expect(fs.existsSync(path.join(out, "b.txt"))).toBe(false);
47
-
48
- fs.rmSync(base, { recursive: true, force: true });
49
- });
50
- });
@@ -1,50 +0,0 @@
1
- const fs = require("fs");
2
-
3
- const path = require("path");
4
-
5
- const plugins = require("../../src/lib/plugins.coffee");
6
-
7
- describe("plugins", () => {
8
- it("runPlugins executes provided plugin functions", async () => {
9
- let called = false;
10
- const tempDir = createTestDir("plugins");
11
- // create a dummy compiled file
12
- const compiled = path.join(tempDir, "out.js");
13
- fs.writeFileSync(compiled, "x");
14
-
15
- const config = {
16
- output: tempDir,
17
- milkee: {
18
- plugins: [
19
- (res) => {
20
- called = true;
21
- return Promise.resolve();
22
- },
23
- ],
24
- },
25
- };
26
- plugins.runPlugins(config, {});
27
-
28
- // wait a tick for async IIFE to run
29
- await new Promise((r) => setTimeout(r, 10));
30
- expect(called).toBe(true);
31
-
32
- fs.rmSync(tempDir, { recursive: true, force: true });
33
- });
34
-
35
- it("warns on invalid plugin entries", async () => {
36
- const tempDir = fs.mkdtempSync(
37
- path.join(require("os").tmpdir(), "milkee-plugins-"),
38
- );
39
- const consola = require("consola");
40
- vi.spyOn(consola, "warn").mockImplementation(() => {});
41
-
42
- const config = { output: tempDir, milkee: { plugins: ["not-fn"] } };
43
- plugins.runPlugins(config, {});
44
- await new Promise((r) => setTimeout(r, 10));
45
-
46
- expect(consola.warn).toHaveBeenCalled();
47
-
48
- fs.rmSync(tempDir, { recursive: true, force: true });
49
- });
50
- });
@@ -1,63 +0,0 @@
1
- const fs = require("fs");
2
- const path = require("path");
3
- const os = require("os");
4
-
5
- const {
6
- executeRefresh,
7
- restoreBackups,
8
- clearBackups,
9
- } = require("../../src/options/refresh.coffee");
10
-
11
- describe("refresh", () => {
12
- it("backs up directory files and restores them", () => {
13
- const base = createTestDir("refresh");
14
- const dir = path.join(base, "out");
15
- fs.mkdirSync(dir, { recursive: true });
16
- const a = path.join(dir, "a.txt");
17
- const b = path.join(dir, "b.txt");
18
- fs.writeFileSync(a, "1");
19
- fs.writeFileSync(b, "2");
20
-
21
- const backupFiles = [];
22
- // preconditions
23
- expect(fs.existsSync(dir)).toBe(true);
24
- expect(fs.existsSync(a)).toBe(true);
25
-
26
- executeRefresh({ output: dir }, backupFiles);
27
-
28
- expect(backupFiles.length).toBe(2);
29
- for (const binfo of backupFiles) {
30
- expect(fs.existsSync(binfo.backup)).toBe(true);
31
- expect(fs.existsSync(binfo.original)).toBe(false);
32
- }
33
-
34
- // restore
35
- restoreBackups(backupFiles);
36
- for (const binfo of backupFiles) {
37
- expect(fs.existsSync(binfo.original)).toBe(true);
38
- expect(fs.existsSync(binfo.backup)).toBe(false);
39
- }
40
-
41
- // cleanup
42
- fs.rmSync(base, { recursive: true, force: true });
43
- });
44
-
45
- it("clearBackups removes backup files", () => {
46
- const base = createTestDir("refresh");
47
- const dir = path.join(base, "out");
48
- fs.mkdirSync(dir, { recursive: true });
49
- const a = path.join(dir, "a.txt");
50
- fs.writeFileSync(a, "1");
51
-
52
- const backupFiles = [];
53
- executeRefresh({ output: dir }, backupFiles);
54
- expect(backupFiles.length).toBeGreaterThan(0);
55
-
56
- clearBackups(backupFiles);
57
- for (const binfo of backupFiles) {
58
- expect(fs.existsSync(binfo.backup)).toBe(false);
59
- }
60
-
61
- fs.rmSync(base, { recursive: true, force: true });
62
- });
63
- });
@@ -1,14 +0,0 @@
1
- const fs = require("fs");
2
- const path = require("path");
3
-
4
- describe("sandbox Windows extended path handling", () => {
5
- it("allows writes when path uses \\?\\ prefix (Windows only)", () => {
6
- if (process.platform !== "win32") return;
7
- const dir = createTestDir("win-prefix");
8
- // simulate Windows extended path prefix
9
- // use Windows extended path prefix with doubled backslashes
10
- const prefixed = "\\\\?\\\\" + path.join(dir, "file.txt");
11
- expect(() => fs.writeFileSync(prefixed, "ok")).not.toThrow();
12
- expect(fs.existsSync(path.join(dir, "file.txt"))).toBe(true);
13
- });
14
- });
@@ -1,70 +0,0 @@
1
- const fs = require("fs");
2
- const path = require("path");
3
- const os = require("os");
4
-
5
- const setup = require("../../src/commands/setup.coffee");
6
- const checks = require("../../src/lib/checks.coffee");
7
- const { CONFIG_FILE } = require("../../src/lib/constants.coffee");
8
-
9
- describe("setup", () => {
10
- let cwd;
11
- beforeEach(() => {
12
- cwd = process.cwd();
13
- vi.spyOn(checks, "checkCoffee").mockImplementation(() => {});
14
- });
15
-
16
- afterEach(() => {
17
- process.chdir(cwd);
18
- vi.restoreAllMocks();
19
- });
20
-
21
- it("creates config file when not present", async () => {
22
- const dir = fs.mkdtempSync(
23
- path.join(require("os").tmpdir(), "milkee-setup-"),
24
- );
25
- process.chdir(dir);
26
-
27
- // re-require to ensure constants.CWD is captured from the new cwd
28
- delete require.cache[require.resolve("../../src/lib/constants.coffee")];
29
- delete require.cache[require.resolve("../../src/commands/setup.coffee")];
30
- const setupLocal = require("../../src/commands/setup.coffee");
31
-
32
- // run setup
33
- await setupLocal();
34
-
35
- const cfgPath = path.join(dir, CONFIG_FILE);
36
- expect(fs.existsSync(cfgPath)).toBe(true);
37
-
38
- try {
39
- fs.rmSync(dir, { recursive: true, force: true });
40
- } catch (e) {
41
- /* ignore */
42
- }
43
- });
44
-
45
- it("does not overwrite when prompt says no", async () => {
46
- const dir = fs.mkdtempSync(
47
- path.join(require("os").tmpdir(), "milkee-setup-"),
48
- );
49
- process.chdir(dir);
50
-
51
- const cfgPath = path.join(dir, CONFIG_FILE);
52
- fs.writeFileSync(cfgPath, "original");
53
-
54
- const consola = require("consola");
55
- vi.spyOn(consola, "prompt").mockResolvedValue(false);
56
-
57
- delete require.cache[require.resolve("../../src/lib/constants.coffee")];
58
- delete require.cache[require.resolve("../../src/commands/setup.coffee")];
59
- const setupLocal = require("../../src/commands/setup.coffee");
60
-
61
- await setupLocal();
62
-
63
- expect(fs.readFileSync(cfgPath, "utf-8")).toBe("original");
64
-
65
- // try cleanup; ignore errors
66
- try {
67
- fs.rmSync(dir, { recursive: true, force: true });
68
- } catch (e) {}
69
- });
70
- });
@@ -1,56 +0,0 @@
1
- const fs = require("fs");
2
- const path = require("path");
3
-
4
- function collectCoffeeFiles(dir) {
5
- let out = [];
6
- const items = fs.readdirSync(dir);
7
- for (const item of items) {
8
- const itemPath = path.join(dir, item);
9
- const stat = fs.statSync(itemPath);
10
- if (stat.isDirectory()) {
11
- out = out.concat(collectCoffeeFiles(itemPath));
12
- } else if (stat.isFile() && itemPath.endsWith(".coffee")) {
13
- out.push(itemPath);
14
- }
15
- }
16
- return out;
17
- }
18
-
19
- const SRC_DIR = path.join(__dirname, "../../src");
20
- let files = [];
21
- try {
22
- files = collectCoffeeFiles(SRC_DIR);
23
- } catch (e) {
24
- // If src missing for some reason, test will fail explicitly below
25
- }
26
-
27
- // Exclude main.coffee and compile.coffee to avoid executing CLI/long-running flows on require
28
- files = files.filter((f) => {
29
- const excluded = [
30
- path.join("src", "main.coffee"),
31
- path.join("src", "commands", "compile.coffee"),
32
- ];
33
- return !excluded.some((e) => f.endsWith(e));
34
- });
35
-
36
- if (files.length === 0) {
37
- it("has .coffee files under src", () => {
38
- throw new Error("No .coffee files found under src");
39
- });
40
- } else {
41
- describe("smoke: require all src .coffee files", () => {
42
- for (const file of files) {
43
- const rel = path.relative(process.cwd(), file);
44
- it(`require ${rel}`, () => {
45
- // clear cache to make require deterministic
46
- try {
47
- delete require.cache[require.resolve(file)];
48
- } catch (e) {}
49
- expect(() => {
50
- const mod = require(file);
51
- expect(mod).not.toBeUndefined();
52
- }).not.toThrow();
53
- });
54
- }
55
- });
56
- }
@@ -1,41 +0,0 @@
1
- const fs = require("fs");
2
- const path = require("path");
3
- const os = require("os");
4
-
5
- const utils = require("../../src/lib/utils.coffee");
6
-
7
- describe("utils", () => {
8
- it("sleep resolves after given time", async () => {
9
- vi.useFakeTimers();
10
- const p = utils.sleep(100);
11
- vi.advanceTimersByTime(100);
12
- await vi.runAllTimersAsync();
13
- await expect(p).resolves.toBeUndefined();
14
- vi.useRealTimers();
15
- });
16
-
17
- it("getCompiledFiles finds .js and .js.map files recursively", () => {
18
- // create directory inside test sandbox
19
- const dir = createTestDir("utils");
20
- const fileA = path.join(dir, "a.js");
21
- const fileB = path.join(dir, "b.js.map");
22
- const fileC = path.join(dir, "c.txt");
23
- const subdir = path.join(dir, "sub");
24
- fs.mkdirSync(subdir);
25
- const fileD = path.join(subdir, "d.js");
26
- fs.writeFileSync(fileA, "console.log(1)");
27
- fs.writeFileSync(fileB, "map");
28
- fs.writeFileSync(fileC, "no");
29
- fs.writeFileSync(fileD, "console.log(2)");
30
- const res = utils.getCompiledFiles(dir);
31
- expect(res).toEqual(expect.arrayContaining([fileA, fileB, fileD]));
32
- fs.rmSync(dir, { recursive: true, force: true });
33
- });
34
-
35
- it("returns empty if path does not exist", () => {
36
- const res = utils.getCompiledFiles(
37
- path.join(os.tmpdir(), "nonexistent-" + Date.now()),
38
- );
39
- expect(res).toEqual([]);
40
- });
41
- });
package/vitest.config.mjs DELETED
@@ -1,14 +0,0 @@
1
- import { defineConfig } from 'vitest/config';
2
-
3
- export default defineConfig({
4
- test: {
5
- environment: 'node',
6
- globals: true,
7
- setupFiles: './test/setup.js',
8
- },
9
- coverage: {
10
- provider: 'v8',
11
- reporter: ['text', 'lcov'],
12
- exclude: ['**/*.coffee'],
13
- },
14
- });