aberlaas 2.1.0 → 2.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/README.md CHANGED
@@ -192,6 +192,129 @@ userland allows user to change the files if they want to change the behavior.
192
192
  configuration files for `aberlaas` itself. They eat their own dog food by
193
193
  referencing the same configs as above.
194
194
 
195
+ ## Tools used and their future
196
+
197
+ ### ESLint
198
+
199
+ ESLint doesn't yet support ESM config files. We'll upgrade to the latest ESLint
200
+ when it does. This should also help fix the issue with Yarn PnP (see below).
201
+
202
+ ### Yarn
203
+
204
+ **tl;dr; We'll move to Yarn PnP once ESLint has support for Flat Configs, and
205
+ default `yarn run` doesn't add a ~1s delay overhead**
206
+
207
+ #### PnP
208
+
209
+
210
+ Aberlaas is using Yarn Berry (v2+) as its main package management tool.
211
+
212
+ Yarn Berry comes with a Plug And Play (PnP) feature that replaces the usage of
213
+ `node_modules` in favor of a `.pnp.cjs` file. Instead of having a very large
214
+ `node_modules` folder, a unique copy of each dependency is stored in the user
215
+ home folder and the `.pnp.cjs` file only keeps references to those folder. This
216
+ makes installing dependencies faster as it needs way less I/O.
217
+
218
+ By ditching the whole `node_modules` principle, it also removes concepts like
219
+ hoisting of dependencies in a monorepo. This, unfortunately, breaks ESLint.
220
+
221
+ ESLint expect all its plugins to be defined as `peerDependencies` at the root of
222
+ a mono-repo, as it will always try to include them from there. It works more or
223
+ less correctly at the best of times, and `aberlaas` already has some hacks
224
+ (including `resolvePluginsRelativeTo`) to work around that.
225
+
226
+ But with PnP, there is no way to make it work correctly, so I will need to wait
227
+ for a better compatibility between ESLint and Yarn 2 before using it.
228
+
229
+ Sources:
230
+ - [#8](https://github.com/yarnpkg/berry/issues/8), the initial case study of Yarn + ESLint
231
+ - [GitHub issue that explicitly explain the problem](https://github.com/yarnpkg/berry/discussions/3909)
232
+ - [`eslint-patch`, an ESLint plugin that hacks around this issue](https://yarnpkg.com/package?name=@rushstack/eslint-patch)
233
+ - [The `resolvePluginsRelativeTo` bandaid from ESLint](https://eslint.org/docs/latest/use/command-line-interface#--resolve-plugins-relative-to)
234
+ - [A nice postmortem of moving to Yarn 2](https://www.dolthub.com/blog/2022-03-18-migrating-to-yarn-2/)
235
+ - [The Flat Config feature in ESLint that should solve the problem](https://eslint.org/docs/latest/use/configure/configuration-files-new)
236
+
237
+ #### Calling binaries from the host
238
+
239
+ In yarn v1, if you install `aberlaas` in your project, all of `aberlaas`
240
+ dependencies (including binaries) were hoisted to the root. This was a design
241
+ flaw, as if several dependencies defined binaries by the same name, they would
242
+ fight in a race condition to take the slot in `node_modules/.bin`.
243
+
244
+ This has been fixed in Yarn Berry, but it also means it's no longer possible to
245
+ call `yarn run eslint` from a repository that includes `aberlaas`, because
246
+ `eslint` is not a direct dependency of the repo.
247
+
248
+ It might be possible to define proxy binaries inside of `aberlaas`, but those
249
+ will incur performance issues as they will need to spawn one more yarn context
250
+ (see below).
251
+
252
+ #### Calling binaries from aberlaas
253
+
254
+ In Yarn V1, I was calling binaries that `aberlaas` depends on (`eslint`, `vitest`,
255
+ etc) by calling `yarn run`. This was adding some overhead but was acceptable.
256
+ Yarn Berry adds even more overhead and it is becoming noticeable.
257
+
258
+ Now, my prefered way it to use the NodeJS API of the dependencies instead of
259
+ their CLI, to limit the overhead. I managed to move most tools to this new
260
+ approach, but sometimes I still need to use the CLI (`vitest` for example has
261
+ awesome live watching and display reloading that I don't think I can easily
262
+ replicate through code).
263
+
264
+ I ran some performance tests to see what would be the fastest way to call
265
+ `vitest` from `aberlaas`
266
+
267
+ ```
268
+ hyperfine \
269
+ "zsh -i -c 'yarn bin vitest && /home/tim/local/www/projects/aberlaas/node_modules/vitest/vitest.mjs --version'" \
270
+ "zsh -i -c 'yarn run vitest --version'" \
271
+ "/home/tim/local/www/projects/aberlaas/node_modules/vitest/vitest.mjs --version" \
272
+ "/home/tim/local/www/projects/aberlaas/node_modules/.bin/vitest --version"
273
+ Benchmark 1: zsh -i -c 'yarn run vitest --version'
274
+ Time (mean ± σ): 1.945 s ± 0.051 s [User: 1.986 s, System: 0.850 s]
275
+ Range (min … max): 1.859 s … 2.018 s 10 runs
276
+
277
+ Benchmark 2: zsh -i -c 'yarn bin vitest && /home/tim/local/www/projects/aberlaas/node_modules/vitest/vitest.mjs --version'
278
+ Time (mean ± σ): 2.108 s ± 0.150 s [User: 2.108 s, System: 0.843 s]
279
+ Range (min … max): 1.930 s … 2.289 s 10 runs
280
+
281
+ Benchmark 3: /home/tim/local/www/projects/aberlaas/node_modules/vitest/vitest.m
282
+ js --version
283
+ Time (mean ± σ): 482.5 ms ± 40.9 ms [User: 448.4 ms, System: 327.2 ms]
284
+ Range (min … max): 442.1 ms … 553.3 ms 10 runs
285
+
286
+ Benchmark 4: /home/tim/local/www/projects/aberlaas/node_modules/.bin/vitest --version
287
+ Time (mean ± σ): 491.9 ms ± 29.6 ms [User: 454.1 ms, System: 331.2 ms]
288
+ Range (min … max): 453.8 ms … 535.4 ms 10 runs
289
+ ```
290
+
291
+ Finding the binary through `yarn bin` then calling it is the slowest, but `yarn
292
+ run` isn't much faster. Directly calling the binary is the fastest, but it's
293
+ path can't be easily guessed (apart from reading and parsing a `package.json`).
294
+ But using the symlinks in `node_modules/.bin` is consistent, barely slower than
295
+ calling the binary directly and much faster than using yarn.
296
+
297
+ This is what I'll be using. Of course, this will break when I'll move to PnP as
298
+ the `node_modules` folder won't be there, but hopefully Yarn will be faster by
299
+ then and I can use `yarn run` reliably.
300
+
301
+ #### Speed
302
+
303
+ With Yarn 2+, calling `yarn run` seem to add ~1s of overhead each time. This is
304
+ a known issue (due to the fact Yarn 2 needs to spawn yarn 1, node and a few
305
+ other layers).
306
+
307
+ 1s is not much in itself, but grows quickly when you have nested `yarn run
308
+ aberlaas` calls that call `yarn run eslint`, and even more when you need to deal
309
+ with monorepos and multiple packages that each have their own yarn scope.
310
+
311
+ There are open issues on the topic, but nothing merged yet:
312
+
313
+ - [#3732](https://github.com/yarnpkg/berry/issues/3732), where it is discussed to make yarn run aware that it's running yarn run
314
+ and keep the same state without spawning new yarns
315
+ - [#2575](https://github.com/yarnpkg/berry/issues/2575), which is the main issue about Yarn performance, with benchmarks against
316
+ npm/npx/yarn v1.
317
+
195
318
  ## Where does the name Aberlaas come from?
196
319
 
197
320
  Aberlaas is the base camp from which all great expedition start in the _La Horde
@@ -205,4 +328,4 @@ For your convenience, `aberlass` and `aberlas` are added as aliases by default.
205
328
 
206
329
  ## Documentation
207
330
 
208
- The complete documentation can be found on https://projects.pixelastic.com/aberlaas/
331
+ The complete documentation can be found on https://projects.pixelastic.com/aberlaas/
@@ -0,0 +1,11 @@
1
+ /**
2
+ * This is a temporary, dummy file to be able to test the top-level
3
+ * compress.run() function. I assume I'll add more compression types in addition
4
+ * to png, so this dummy compress is just to test that all compress are
5
+ * correctly called, but it doesn't do anything
6
+ **/
7
+ export default {
8
+ async run() {
9
+ return true;
10
+ },
11
+ };
@@ -2,11 +2,13 @@ import _ from 'golgoth/lodash.js';
2
2
  import pMap from 'golgoth/pMap.js';
3
3
  import consoleError from 'firost/consoleError.js';
4
4
  import firostError from 'firost/error.js';
5
- import helper from '../../helper.js';
5
+ import compressPng from './png.js';
6
+ import compressDummy from './dummy.js';
6
7
 
7
8
  export default {
8
9
  types: {
9
- png: './png.js',
10
+ png: compressPng,
11
+ dummy: compressDummy,
10
12
  },
11
13
  /**
12
14
  * Wrapper to compress all supported formats
@@ -22,7 +24,7 @@ export default {
22
24
  await pMap(typesToCompress, async (type) => {
23
25
  try {
24
26
  const userPatterns = _.get(cliArgs, '_');
25
- const compresser = require(this.types[type]);
27
+ const compresser = this.types[type];
26
28
 
27
29
  await compresser.run(userPatterns);
28
30
  } catch (error) {
@@ -37,16 +39,5 @@ export default {
37
39
 
38
40
  return true;
39
41
  },
40
- /**
41
- * Find all relevant files of the specified extension in the host
42
- * Note: Should be used by child classes
43
- * @param {Array} safeListExtension List of allowed extensions to keep
44
- * @param {Array} userPatterns Patterns to narrow the search down
45
- * @returns {Array} Array of files
46
- **/
47
- async getInputFiles(safeListExtension, userPatterns = []) {
48
- const inputPatterns = _.isEmpty(userPatterns) ? '.' : userPatterns;
49
- return await helper.findHostFiles(inputPatterns, safeListExtension);
50
- },
51
42
  __consoleError: consoleError,
52
43
  };
@@ -1,4 +1,5 @@
1
- import lint from './index.js';
1
+ import helper from '../../helper.js';
2
+ import _ from 'golgoth/lodash.js';
2
3
  import run from 'firost/run.js';
3
4
  import which from 'firost/which.js';
4
5
  import firostError from 'firost/error.js';
@@ -10,15 +11,19 @@ export default {
10
11
  * @returns {Array} Array of files
11
12
  **/
12
13
  async getInputFiles(userPatterns) {
13
- return await lint.getInputFiles(['.png'], userPatterns);
14
+ const filePatterns = _.isEmpty(userPatterns)
15
+ ? ['./**/*.png']
16
+ : userPatterns;
17
+ return await helper.findHostFiles(filePatterns, ['.png']);
14
18
  },
19
+
15
20
  /**
16
- * Check if the binary is available in the $PATH
17
- * @returns {boolean} True if available, false otherwise
21
+
22
+ * Returns path to the binary to execute
23
+ * @returns {string|boolean} Path to the binary, or false if not found
18
24
  **/
19
- async hasBin() {
20
- const binary = await which('pngmin');
21
- return !!binary;
25
+ async getBinaryPath() {
26
+ return await this.__which('pngmin');
22
27
  },
23
28
  /**
24
29
  * Compress files
@@ -27,16 +32,20 @@ export default {
27
32
  **/
28
33
  async run(userPatterns) {
29
34
  // Stop early if no bin
30
- if (!(await this.hasBin())) {
35
+ const binaryPath = await this.getBinaryPath();
36
+ if (!binaryPath) {
31
37
  return true;
32
38
  }
33
39
 
34
40
  try {
35
41
  const files = await this.getInputFiles(userPatterns);
36
- const command = `pngmin ${files.join(' ')}`;
42
+ const command = `${binaryPath} ${files.join(' ')}`;
37
43
  await run(command, { stdout: false });
38
44
  } catch (error) {
39
45
  throw firostError('PngCompressError', error.message);
40
46
  }
47
+
48
+ return true;
41
49
  },
50
+ __which: which,
42
51
  };
@@ -45,6 +45,9 @@ export default {
45
45
  * default rules and overwrite them as they see fit
46
46
  **/
47
47
  async addConfigFiles() {
48
+ // Yarn
49
+ await this.copyToHost('templates/_yarnrc.yml', '.yarnrc.yml');
50
+
48
51
  // ESLint
49
52
  await this.copyToHost('templates/_eslintrc.cjs', '.eslintrc.cjs');
50
53
  await this.copyToHost('templates/_eslintignore.conf', '.eslintignore');
@@ -106,6 +109,7 @@ export default {
106
109
  async addScripts() {
107
110
  const defaultScripts = [
108
111
  { key: 'ci', value: 'scripts/ci' },
112
+ { key: 'compress', value: 'scripts/compress' },
109
113
  { key: 'lint', value: 'scripts/lint' },
110
114
  { key: 'lint:fix', value: 'scripts/lint-fix' },
111
115
  { key: 'release', value: 'scripts/release' },
@@ -1,8 +1,8 @@
1
1
  import helper from '../../helper.js';
2
2
  import { fix as prettierFix } from './helpers/prettier.js';
3
+ import stylelint from 'stylelint';
3
4
  import _ from 'golgoth/lodash.js';
4
5
  import firostError from 'firost/error.js';
5
- import run from 'firost/run.js';
6
6
 
7
7
  export default {
8
8
  /**
@@ -11,37 +11,45 @@ export default {
11
11
  * @returns {Array} Array of files
12
12
  **/
13
13
  async getInputFiles(userPatterns) {
14
- return await helper.findHostFiles(userPatterns, ['.css']);
14
+ const filePatterns = _.isEmpty(userPatterns)
15
+ ? ['./**/*.css']
16
+ : userPatterns;
17
+ return await helper.findHostFiles(filePatterns, ['.css']);
15
18
  },
16
19
  /**
17
20
  * Lint all files and display results.
18
21
  * @param {Array} userPatterns Patterns to narrow the search down
19
22
  * @param {string} userConfigFile Custom config file to use
23
+ * @param {object} userOptions Options to pass to ESLint, including fix
20
24
  * @returns {boolean} True on success
21
25
  **/
22
- async run(userPatterns, userConfigFile) {
26
+ async run(userPatterns, userConfigFile, userOptions = {}) {
27
+ // Options
28
+ const options = { fix: false, ...userOptions };
29
+
30
+ // Files
23
31
  const files = await this.getInputFiles(userPatterns);
24
32
  if (_.isEmpty(files)) {
25
33
  return true;
26
34
  }
27
35
 
36
+ // Config
28
37
  const configFile = await helper.configFile(
29
38
  userConfigFile,
30
39
  '.stylelintrc.cjs',
31
40
  'configs/stylelint.cjs',
32
41
  );
33
- const binary = await helper.which('stylelint');
34
- const options = [...files, '--color', '--config', configFile];
35
- try {
36
- await run(`${binary} ${options.join(' ')}`, { stdout: false });
37
- } catch (error) {
38
- // If it fails because no files passed actually exists, it's not really
39
- // a failure
40
- const errorMessage = error.stdout;
41
- if (_.startsWith(errorMessage, 'Error: No files matching the pattern')) {
42
- return true;
43
- }
44
- throw firostError('ERROR_CSS_LINT', error.stdout);
42
+ const config = await helper.import(configFile);
43
+
44
+ const result = await stylelint.lint({
45
+ config,
46
+ files,
47
+ formatter: 'string',
48
+ ...options,
49
+ });
50
+
51
+ if (result.errored) {
52
+ throw firostError('ERROR_CSS_LINT', result.output);
45
53
  }
46
54
  return true;
47
55
  },
@@ -57,11 +65,9 @@ export default {
57
65
  return true;
58
66
  }
59
67
  // Try to pretiffy as much as we can
60
- await this.__prettierFix(files);
68
+ await prettierFix(files);
61
69
  // Still run a lint on it so it can fail if not everything is fixed
62
- await this.run(userPatterns, userConfigFile);
70
+ await this.run(userPatterns, userConfigFile, { fix: true });
63
71
  return true;
64
72
  },
65
- __prettierFix: prettierFix,
66
- __run: run,
67
73
  };
@@ -1,25 +1,53 @@
1
+ import * as prettier from 'prettier';
1
2
  import helper from '../../../helper.js';
2
- import run from 'firost/run.js';
3
3
  import firostError from 'firost/error.js';
4
+ import write from 'firost/write.js';
5
+ import read from 'firost/read.js';
6
+ import pMap from 'golgoth/pMap.js';
7
+ import _ from 'golgoth/lodash.js';
4
8
 
5
9
  /**
6
10
  * Fix all files using prettier
7
- * Note: Will be called by child classes
8
- * Note: Prettier does not output any information as to why it failed, so
9
- * we'll manually run the command on each file individually so we can catch
10
- * the file that errors and display it
11
11
  * @param {Array} inputFiles Files to auto-fix
12
12
  **/
13
13
  export async function fix(inputFiles) {
14
- const binary = await helper.which('prettier');
15
- const options = ['--write', ...inputFiles];
16
- try {
17
- const command = `${binary} ${options.join(' ')}`;
18
- await run(command, { stdout: false });
19
- } catch (err) {
14
+ // Config file
15
+ const configFile = await helper.configFile(
16
+ null,
17
+ '.prettierrc.cjs',
18
+ 'lib/configs/prettier.cjs',
19
+ );
20
+ const config = await prettier.resolveConfig(configFile);
21
+
22
+ const errors = [];
23
+
24
+ // Read all files, run them through the formatter and save them back to disk
25
+ // If any emits error, store the errors and display them all in one output
26
+ await pMap(
27
+ inputFiles,
28
+ async (filepath) => {
29
+ try {
30
+ const content = await read(filepath);
31
+ const options = { ...config, filepath };
32
+ const result = await prettier.format(content, options);
33
+ await write(result, filepath);
34
+ } catch (error) {
35
+ const message = error.toString();
36
+ errors.push({ filepath, message });
37
+ }
38
+ },
39
+ { concurrency: 10 },
40
+ );
41
+
42
+ if (!_.isEmpty(errors)) {
43
+ let formattedErrors = '';
44
+ _.each(errors, (error) => {
45
+ formattedErrors = `${formattedErrors}${error.filepath}\n\n${error.message}\n\n`;
46
+ });
47
+
20
48
  throw firostError(
21
49
  'LINT_ERROR_FIX_PRETTIER',
22
- 'Some files could not be automatically fixed.\nPlease run `yarn run lint` to further debug',
50
+ `Some files could not be automatically fixed:\n\n${formattedErrors}`,
23
51
  );
24
52
  }
25
53
  }
@@ -1,7 +1,6 @@
1
1
  import helper from '../../helper.js';
2
2
  import _ from 'golgoth/lodash.js';
3
3
  import firostError from 'firost/error.js';
4
- import run from 'firost/run.js';
5
4
  import { ESLint } from 'eslint';
6
5
 
7
6
  export default {
@@ -11,7 +10,8 @@ export default {
11
10
  * @returns {Array} Array of files
12
11
  **/
13
12
  async getInputFiles(userPatterns) {
14
- return await helper.findHostFiles(userPatterns, ['.js']);
13
+ const filePatterns = _.isEmpty(userPatterns) ? ['./**/*.js'] : userPatterns;
14
+ return await helper.findHostFiles(filePatterns, ['.js']);
15
15
  },
16
16
  /**
17
17
  * Lint all files and display results.
@@ -70,5 +70,4 @@ export default {
70
70
  async fix(userPatterns, userConfigFile) {
71
71
  return await this.run(userPatterns, userConfigFile, { fix: true });
72
72
  },
73
- __run: run,
74
73
  };
@@ -13,7 +13,10 @@ export default {
13
13
  * @returns {Array} Array of files
14
14
  **/
15
15
  async getInputFiles(userPatterns) {
16
- return await helper.findHostFiles(userPatterns, ['.json']);
16
+ const filePatterns = _.isEmpty(userPatterns)
17
+ ? ['./**/*.json']
18
+ : userPatterns;
19
+ return await helper.findHostFiles(filePatterns, ['.json']);
17
20
  },
18
21
  /**
19
22
  * Lint all files and display results.
@@ -30,7 +33,7 @@ export default {
30
33
  const errorMessages = [];
31
34
  await pMap(files, async (filepath) => {
32
35
  try {
33
- this.__parse(await read(filepath));
36
+ JSON.parse(await read(filepath));
34
37
  } catch (error) {
35
38
  hasErrors = true;
36
39
  const relativePath = path.relative(helper.hostRoot(), filepath);
@@ -54,8 +57,6 @@ export default {
54
57
  if (_.isEmpty(files)) {
55
58
  return true;
56
59
  }
57
- await this.__prettierFix(files);
60
+ await prettierFix(files);
58
61
  },
59
- __prettierFix: prettierFix,
60
- __parse: JSON.parse,
61
62
  };
@@ -14,7 +14,10 @@ export default {
14
14
  * @returns {Array} Array of files
15
15
  **/
16
16
  async getInputFiles(userPatterns) {
17
- return await helper.findHostFiles(userPatterns, ['.yml', '.yaml']);
17
+ const filePatterns = _.isEmpty(userPatterns)
18
+ ? ['./**/*.yml', './**/*.yaml']
19
+ : userPatterns;
20
+ return await helper.findHostFiles(filePatterns, ['.yml', '.yaml']);
18
21
  },
19
22
  /**
20
23
  * Lint all files and display results.
@@ -32,7 +35,7 @@ export default {
32
35
  await pMap(files, async (filepath) => {
33
36
  const input = await read(filepath);
34
37
  try {
35
- await this.__lint(input);
38
+ await yamlLint.lint(input);
36
39
  } catch (error) {
37
40
  hasErrors = true;
38
41
  const relativePath = path.relative(helper.hostRoot(), filepath);
@@ -56,8 +59,6 @@ export default {
56
59
  if (_.isEmpty(files)) {
57
60
  return true;
58
61
  }
59
- await this.__prettierFix(files);
62
+ await prettierFix(files);
60
63
  },
61
- __prettierFix: prettierFix,
62
- __lint: yamlLint.lint,
63
64
  };
@@ -17,9 +17,8 @@ export default {
17
17
  **/
18
18
  async run(cliArgs) {
19
19
  const options = await this.vitestCliOptions(cliArgs);
20
- const binary = await helper.which('vitest');
21
20
 
22
- await run(`${binary} ${options.join(' ')}`, { stdin: true });
21
+ await run(`yarn run vitest ${options.join(' ')}`, { stdin: true });
23
22
  return true;
24
23
  },
25
24
 
@@ -15,6 +15,23 @@ module.exports = {
15
15
  'plugin:import/recommended',
16
16
  'plugin:prettier/recommended',
17
17
  ],
18
+ settings: {
19
+ // eslint-plugin-import doesn't currently support the "exports" syntax in
20
+ // package.json. This allow mapping between custom entrypoints and
21
+ // files on disk.
22
+ // For example, it doesn't understand "import * from 'vitest/config';" as
23
+ // "vitest/config/" isn't really an existing filepath, but a mapping defined
24
+ // in vitest package.json
25
+ //
26
+ // Until this is fixed (see
27
+ // https://github.com/import-js/eslint-plugin-import/issues/2430)
28
+ // we manually define the most common extensions
29
+ 'import/resolver': {
30
+ node: {
31
+ extensions: ['.js', '.cjs', '.mjs', '.d.ts'],
32
+ },
33
+ },
34
+ },
18
35
  plugins: ['jsdoc', 'prettier'],
19
36
  rules: {
20
37
  'dot-notation': ['error'],
@@ -98,10 +115,8 @@ module.exports = {
98
115
  testName: false,
99
116
  // Shorter method names
100
117
  fit: false,
101
- ftest: false,
102
118
  fdescribe: false,
103
119
  xit: false,
104
- xtest: false,
105
120
  xdescribe: false,
106
121
 
107
122
  captureOutput: false,
@@ -113,7 +128,7 @@ module.exports = {
113
128
  ],
114
129
  rules: {
115
130
  'no-restricted-globals': [
116
- 'warn',
131
+ 'error',
117
132
  {
118
133
  name: 'fit',
119
134
  message: 'No focused test',
@@ -18,6 +18,12 @@ if (pkg.scripts && pkg.scripts.lint) {
18
18
  result['*.json'] = ['yarn run lint:fix --json'];
19
19
  result['*.js'] = ['yarn run lint:fix --js'];
20
20
  }
21
+
22
+ // Compressing images
23
+ if (pkg.scripts && pkg.scripts.compress) {
24
+ result['*.png'] = ['yarn run compress --png'];
25
+ }
26
+
21
27
  // Testing changed js files
22
28
  if (pkg.scripts && pkg.scripts.test) {
23
29
  result['./lib/**/*.js'] = ['yarn run test --failFast --related'];
package/helper.js CHANGED
@@ -159,4 +159,17 @@ export default {
159
159
  async yarnRun(scriptName) {
160
160
  return await run(`yarn run ${scriptName}`, { cwd: this.hostRoot() });
161
161
  },
162
+ /**
163
+ * Dynamically import a file
164
+ * This is a wrapper around the default import, but bypasses the cache
165
+ * It makes sure importing a file several times always loads the last version
166
+ * TODO: This should probably be moved in firost once firost is moved to ESM
167
+ * @param {string} filepath Path to the file to load
168
+ * @returns {object} Content of the loaded file
169
+ **/
170
+ async import(filepath) {
171
+ const content = await import(`${filepath}?cacheBusting=${Date.now()}`);
172
+
173
+ return content.default || content;
174
+ },
162
175
  };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "aberlaas",
3
3
  "type": "module",
4
4
  "description": "Scaffold your JavaScript projects with tests, lint and release scripts",
5
- "version": "2.1.0",
5
+ "version": "2.2.0",
6
6
  "repository": "pixelastic/aberlaas",
7
7
  "homepage": "https://projects.pixelastic.com/aberlaas/",
8
8
  "author": "Tim Carry (@pixelastic)",
@@ -33,15 +33,14 @@
33
33
  "configs/",
34
34
  "templates/"
35
35
  ],
36
- "bin": {
37
- "aberlaas": "bin/aberlaas.js"
38
- },
36
+ "bin": "bin/aberlaas.js",
39
37
  "dependencies": {
40
38
  "@octokit/rest": "18.12.0",
41
39
  "ci-info": "3.9.0",
42
40
  "dedent": "1.5.1",
43
41
  "eslint": "8.54.0",
44
42
  "eslint-config-prettier": "9.0.0",
43
+ "eslint-plugin-import": "2.29.0",
45
44
  "eslint-plugin-jsdoc": "46.9.0",
46
45
  "eslint-plugin-n": "16.3.1",
47
46
  "eslint-plugin-prettier": "5.0.1",
@@ -77,9 +76,5 @@
77
76
  "test:watch": "../scripts/lib/test-watch",
78
77
  "postinstall": "./scripts/postinstall"
79
78
  },
80
- "devDependencies": {
81
- "eslint-plugin-import": "2.29.0",
82
- "eslint-plugin-vitest": "0.3.10"
83
- },
84
- "gitHead": "5d021891b8ba9c030747a3bc7bf20d86c85422db"
79
+ "gitHead": "bbf9f5024d007deca00abf451ddf879c876d80aa"
85
80
  }
@@ -0,0 +1,9 @@
1
+ compressionLevel: 0
2
+
3
+ defaultSemverRangePrefix: ''
4
+
5
+ enableGlobalCache: true
6
+
7
+ nmMode: hardlinks-local
8
+
9
+ nodeLinker: node-modules
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env sh
2
+ set -e
3
+
4
+ aberlaas compress "$@"
@@ -1,52 +0,0 @@
1
- const path = require('path');
2
- const jestExtendedPath = path.resolve(__dirname, './jest-extended.cjs');
3
- const setupFileAfterEnv = path.resolve(__dirname, './setupFileAfterEnv.cjs');
4
- const testEnvironment = path.resolve(__dirname, './testEnvironment.cjs');
5
- module.exports = {
6
- // Custom environment that automatically set testName and allow for --failFast
7
- testEnvironment,
8
-
9
- // Use additional Jest plugins
10
- setupFilesAfterEnv: [jestExtendedPath, setupFileAfterEnv],
11
-
12
- // We need to set rootDir to the host directory, otherwise Jest will assume
13
- // test are to be looked for in the directory that contains the config (ie.
14
- // aberlaas).
15
- rootDir: process.cwd(),
16
-
17
- // Tests should be in a __tests__ folder
18
- testMatch: ['**/__tests__/**/*.js?(x)'],
19
-
20
- // By default watch mode watches for changes in all directories, and whenever
21
- // a test file or associated code file changes, it re-runs tests.
22
- // This can cause useless re-render of tests when it catches changes in
23
- // directories like ./tmp, etc, so we exclude such directories
24
- // watchPathIgnorePatterns only accept RegExp and not globs, so we have to
25
- // deal with ((.*)/)? to express any depth of folders
26
- watchPathIgnorePatterns: [
27
- '<rootDir>/((.*)/)?node_modules/',
28
- '<rootDir>/((.*)/)?tmp/',
29
- '<rootDir>/((.*)/)?templates/',
30
- ],
31
- testPathIgnorePatterns: [
32
- '<rootDir>/((.*)/)?fixtures/',
33
- '<rootDir>/((.*)/)?tmp/',
34
- '<rootDir>/((.*)/)?templates/',
35
- ],
36
-
37
- // moduleNameMapper: {
38
- // // When using lodash-es (which is treeshakeable, thus preferable in front-end
39
- // // envs), it will fail in tests as it isn't compiled to ES5.
40
- // // So, we make jest load the full lodash instead. Note that the lib still
41
- // // needs to be added as a devDependency
42
- // '^lodash-es$': 'lodash',
43
- // },
44
-
45
- // Make sure we don't transform source code and use the default ESM code
46
- transform: {},
47
-
48
- bail: true,
49
-
50
- resetMocks: true,
51
- restoreMocks: true,
52
- };
@@ -1,3 +0,0 @@
1
- // This is a proxy to make sure we load the plugin no matter where it is
2
- // installed
3
- module.exports = require('jest-extended');
@@ -1,13 +0,0 @@
1
- /* eslint-disable jest/no-jasmine-globals,no-global-assign */
2
- const captureOutput = require('firost/captureOutput');
3
- const dedent = require('dedent');
4
-
5
- /**
6
- * Add new globals to each test file:
7
- * - captureOutput accepts a callback that will be executed with all output
8
- * silenced and returned instead
9
- * - dedent (https://github.com/dmnd/dedent) to enter multiline strings without
10
- * the indentation
11
- **/
12
- global.captureOutput = captureOutput;
13
- global.dedent = dedent;
@@ -1,14 +0,0 @@
1
- /**
2
- * Jest environments are sandboxed by test suite. To be able to fail fast and
3
- * stop all tests as soon as one of them failed, we are using this file as an
4
- * external singleton.
5
- **/
6
- module.exports = {
7
- __skipAllTests: false,
8
- skipAllTests() {
9
- this.__skipAllTests = true;
10
- },
11
- shouldSkipAllTests() {
12
- return this.__skipAllTests;
13
- },
14
- };
@@ -1,46 +0,0 @@
1
- const NodeEnvironment = require('jest-environment-node');
2
- const shouldFailFast = process.env.ABERLAAS_TEST_FAIL_FAST;
3
- const sharedState = require('./sharedState.cjs');
4
-
5
- class AberlaasEnvironment extends NodeEnvironment {
6
- async handleTestEvent(event, _state) {
7
- this.failFast(event);
8
- this.setTestName(event);
9
- }
10
- /**
11
- * If one test fails, we skip all other tests
12
- * @param {object} event As fired by handleTestEvent
13
- */
14
- failFast(event) {
15
- // Do nothing if --failFast is not passed
16
- if (!shouldFailFast) {
17
- return;
18
- }
19
-
20
- // Whenever a test is failing, we update the shared state to skip all other
21
- // tests
22
- const eventName = event.name;
23
- const isTestFailing = eventName == 'test_fn_failure';
24
- const isTestStarting = eventName === 'test_start';
25
-
26
- if (isTestFailing) {
27
- sharedState.skipAllTests();
28
- }
29
- if (isTestStarting && sharedState.shouldSkipAllTests()) {
30
- event.test.mode = 'skip';
31
- }
32
- }
33
- /**
34
- * When a test starts, we set the global variable testName to the name of the
35
- * test
36
- * @param {object} event As fired by handleTestEvent
37
- **/
38
- setTestName(event) {
39
- const eventName = event.name;
40
- if (eventName === 'test_start') {
41
- this.global.testName = event.test.name;
42
- }
43
- }
44
- }
45
-
46
- module.exports = AberlaasEnvironment;
package/configs/jest.cjs DELETED
@@ -1 +0,0 @@
1
- module.exports = require('./jest/index.cjs');
@@ -1,4 +0,0 @@
1
- const config = require('aberlaas/configs/jest.cjs');
2
- module.exports = {
3
- ...config,
4
- };