aberlaas 2.7.0 → 2.9.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 +20 -125
- package/commands/init/helper.js +192 -0
- package/commands/init/index.js +16 -388
- package/commands/init/module.js +111 -0
- package/commands/init/monorepo.js +261 -0
- package/commands/precommit/index.js +3 -0
- package/commands/test/index.js +47 -14
- package/configs/eslint.cjs +5 -0
- package/configs/lintstaged.js +1 -1
- package/configs/vite.js +12 -3
- package/package.json +4 -3
- package/templates/_circleci/config.yml +13 -4
package/README.md
CHANGED
|
@@ -161,6 +161,24 @@ what most free CI tier offer). If you have access to higher end machines, you
|
|
|
161
161
|
can update this value by passing the `--cpu-count=X` flag to your `aberlaas ci`
|
|
162
162
|
call.
|
|
163
163
|
|
|
164
|
+
### Auto-Releasing
|
|
165
|
+
|
|
166
|
+
As an optional feature, you can have aberlaas automatically release a new
|
|
167
|
+
version of your module from the CI environment when relevant.
|
|
168
|
+
|
|
169
|
+
The CI will then check all the commits since the last release. If any commit is
|
|
170
|
+
a `feat()` it will release a new minor version; it any commit is a `fix()` it
|
|
171
|
+
will release a new patch version. For major release, you'll have to do it
|
|
172
|
+
manually.
|
|
173
|
+
|
|
174
|
+
This option is not enabled by default. If you need it, you need to follow those
|
|
175
|
+
steps:
|
|
176
|
+
|
|
177
|
+
- Run `aberlaas setup --auto-release`. It will setup the required `ENV` variables
|
|
178
|
+
and ssh keys
|
|
179
|
+
- Update your `aberlaas ci` script to `aberlaas ci --auto-release`
|
|
180
|
+
- Uncomment the `add_ssh_keys` in your `.circleci.yml` file
|
|
181
|
+
|
|
164
182
|
## File structure
|
|
165
183
|
|
|
166
184
|
`./lib/configs` contain the default configuration for all the tools. They are
|
|
@@ -170,133 +188,10 @@ exported by the package and thus can be `require`d in userland.
|
|
|
170
188
|
extends the configuration exported in the previous files. Copying files to
|
|
171
189
|
userland allows user to change the files if they want to change the behavior.
|
|
172
190
|
|
|
173
|
-
`.eslintrc.js`,
|
|
191
|
+
`.eslintrc.js`, `.stylelintrc.js` and `jest.config.js` are local
|
|
174
192
|
configuration files for `aberlaas` itself. They eat their own dog food by
|
|
175
193
|
referencing the same configs as above.
|
|
176
194
|
|
|
177
|
-
## Tools used and their future
|
|
178
|
-
|
|
179
|
-
### ESLint
|
|
180
|
-
|
|
181
|
-
ESLint doesn't yet support ESM config files. We'll upgrade to the latest ESLint
|
|
182
|
-
when it does. This should also help fix the issue with Yarn PnP (see below).
|
|
183
|
-
|
|
184
|
-
### Yarn
|
|
185
|
-
|
|
186
|
-
**tl;dr; We'll move to Yarn PnP once ESLint has support for Flat Configs, and
|
|
187
|
-
default `yarn run` doesn't add a ~1s delay overhead**
|
|
188
|
-
|
|
189
|
-
#### PnP
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
Aberlaas is using Yarn Berry (v2+) as its main package management tool.
|
|
193
|
-
|
|
194
|
-
Yarn Berry comes with a Plug And Play (PnP) feature that replaces the usage of
|
|
195
|
-
`node_modules` in favor of a `.pnp.cjs` file. Instead of having a very large
|
|
196
|
-
`node_modules` folder, a unique copy of each dependency is stored in the user
|
|
197
|
-
home folder and the `.pnp.cjs` file only keeps references to those folder. This
|
|
198
|
-
makes installing dependencies faster as it needs way less I/O.
|
|
199
|
-
|
|
200
|
-
By ditching the whole `node_modules` principle, it also removes concepts like
|
|
201
|
-
hoisting of dependencies in a monorepo. This, unfortunately, breaks ESLint.
|
|
202
|
-
|
|
203
|
-
ESLint expect all its plugins to be defined as `peerDependencies` at the root of
|
|
204
|
-
a mono-repo, as it will always try to include them from there. It works more or
|
|
205
|
-
less correctly at the best of times, and `aberlaas` already has some hacks
|
|
206
|
-
(including `resolvePluginsRelativeTo`) to work around that.
|
|
207
|
-
|
|
208
|
-
But with PnP, there is no way to make it work correctly, so I will need to wait
|
|
209
|
-
for a better compatibility between ESLint and Yarn 2 before using it.
|
|
210
|
-
|
|
211
|
-
Sources:
|
|
212
|
-
- [#8](https://github.com/yarnpkg/berry/issues/8), the initial case study of Yarn + ESLint
|
|
213
|
-
- [GitHub issue that explicitly explain the problem](https://github.com/yarnpkg/berry/discussions/3909)
|
|
214
|
-
- [`eslint-patch`, an ESLint plugin that hacks around this issue](https://yarnpkg.com/package?name=@rushstack/eslint-patch)
|
|
215
|
-
- [The `resolvePluginsRelativeTo` bandaid from ESLint](https://eslint.org/docs/latest/use/command-line-interface#--resolve-plugins-relative-to)
|
|
216
|
-
- [A nice postmortem of moving to Yarn 2](https://www.dolthub.com/blog/2022-03-18-migrating-to-yarn-2/)
|
|
217
|
-
- [The Flat Config feature in ESLint that should solve the problem](https://eslint.org/docs/latest/use/configure/configuration-files-new)
|
|
218
|
-
|
|
219
|
-
#### Calling binaries from the host
|
|
220
|
-
|
|
221
|
-
In yarn v1, if you install `aberlaas` in your project, all of `aberlaas`
|
|
222
|
-
dependencies (including binaries) were hoisted to the root. This was a design
|
|
223
|
-
flaw, as if several dependencies defined binaries by the same name, they would
|
|
224
|
-
fight in a race condition to take the slot in `node_modules/.bin`.
|
|
225
|
-
|
|
226
|
-
This has been fixed in Yarn Berry, but it also means it's no longer possible to
|
|
227
|
-
call `yarn run eslint` from a repository that includes `aberlaas`, because
|
|
228
|
-
`eslint` is not a direct dependency of the repo.
|
|
229
|
-
|
|
230
|
-
It might be possible to define proxy binaries inside of `aberlaas`, but those
|
|
231
|
-
will incur performance issues as they will need to spawn one more yarn context
|
|
232
|
-
(see below).
|
|
233
|
-
|
|
234
|
-
#### Calling binaries from aberlaas
|
|
235
|
-
|
|
236
|
-
In Yarn V1, I was calling binaries that `aberlaas` depends on (`eslint`, `vitest`,
|
|
237
|
-
etc) by calling `yarn run`. This was adding some overhead but was acceptable.
|
|
238
|
-
Yarn Berry adds even more overhead and it is becoming noticeable.
|
|
239
|
-
|
|
240
|
-
Now, my prefered way it to use the NodeJS API of the dependencies instead of
|
|
241
|
-
their CLI, to limit the overhead. I managed to move most tools to this new
|
|
242
|
-
approach, but sometimes I still need to use the CLI (`vitest` for example has
|
|
243
|
-
awesome live watching and display reloading that I don't think I can easily
|
|
244
|
-
replicate through code).
|
|
245
|
-
|
|
246
|
-
I ran some performance tests to see what would be the fastest way to call
|
|
247
|
-
`vitest` from `aberlaas`
|
|
248
|
-
|
|
249
|
-
```
|
|
250
|
-
hyperfine \
|
|
251
|
-
"zsh -i -c 'yarn bin vitest && /home/tim/local/www/projects/aberlaas/node_modules/vitest/vitest.mjs --version'" \
|
|
252
|
-
"zsh -i -c 'yarn run vitest --version'" \
|
|
253
|
-
"/home/tim/local/www/projects/aberlaas/node_modules/vitest/vitest.mjs --version" \
|
|
254
|
-
"/home/tim/local/www/projects/aberlaas/node_modules/.bin/vitest --version"
|
|
255
|
-
Benchmark 1: zsh -i -c 'yarn run vitest --version'
|
|
256
|
-
Time (mean ± σ): 1.945 s ± 0.051 s [User: 1.986 s, System: 0.850 s]
|
|
257
|
-
Range (min … max): 1.859 s … 2.018 s 10 runs
|
|
258
|
-
|
|
259
|
-
Benchmark 2: zsh -i -c 'yarn bin vitest && /home/tim/local/www/projects/aberlaas/node_modules/vitest/vitest.mjs --version'
|
|
260
|
-
Time (mean ± σ): 2.108 s ± 0.150 s [User: 2.108 s, System: 0.843 s]
|
|
261
|
-
Range (min … max): 1.930 s … 2.289 s 10 runs
|
|
262
|
-
|
|
263
|
-
Benchmark 3: /home/tim/local/www/projects/aberlaas/node_modules/vitest/vitest.m
|
|
264
|
-
js --version
|
|
265
|
-
Time (mean ± σ): 482.5 ms ± 40.9 ms [User: 448.4 ms, System: 327.2 ms]
|
|
266
|
-
Range (min … max): 442.1 ms … 553.3 ms 10 runs
|
|
267
|
-
|
|
268
|
-
Benchmark 4: /home/tim/local/www/projects/aberlaas/node_modules/.bin/vitest --version
|
|
269
|
-
Time (mean ± σ): 491.9 ms ± 29.6 ms [User: 454.1 ms, System: 331.2 ms]
|
|
270
|
-
Range (min … max): 453.8 ms … 535.4 ms 10 runs
|
|
271
|
-
```
|
|
272
|
-
|
|
273
|
-
Finding the binary through `yarn bin` then calling it is the slowest, but `yarn
|
|
274
|
-
run` isn't much faster. Directly calling the binary is the fastest, but it's
|
|
275
|
-
path can't be easily guessed (apart from reading and parsing a `package.json`).
|
|
276
|
-
But using the symlinks in `node_modules/.bin` is consistent, barely slower than
|
|
277
|
-
calling the binary directly and much faster than using yarn.
|
|
278
|
-
|
|
279
|
-
This is what I'll be using. Of course, this will break when I'll move to PnP as
|
|
280
|
-
the `node_modules` folder won't be there, but hopefully Yarn will be faster by
|
|
281
|
-
then and I can use `yarn run` reliably.
|
|
282
|
-
|
|
283
|
-
#### Speed
|
|
284
|
-
|
|
285
|
-
With Yarn 2+, calling `yarn run` seem to add ~1s of overhead each time. This is
|
|
286
|
-
a known issue (due to the fact Yarn 2 needs to spawn yarn 1, node and a few
|
|
287
|
-
other layers).
|
|
288
|
-
|
|
289
|
-
1s is not much in itself, but grows quickly when you have nested `yarn run
|
|
290
|
-
aberlaas` calls that call `yarn run eslint`, and even more when you need to deal
|
|
291
|
-
with monorepos and multiple packages that each have their own yarn scope.
|
|
292
|
-
|
|
293
|
-
There are open issues on the topic, but nothing merged yet:
|
|
294
|
-
|
|
295
|
-
- [#3732](https://github.com/yarnpkg/berry/issues/3732), where it is discussed to make yarn run aware that it's running yarn run
|
|
296
|
-
and keep the same state without spawning new yarns
|
|
297
|
-
- [#2575](https://github.com/yarnpkg/berry/issues/2575), which is the main issue about Yarn performance, with benchmarks against
|
|
298
|
-
npm/npx/yarn v1.
|
|
299
|
-
|
|
300
195
|
## Where does the name Aberlaas come from?
|
|
301
196
|
|
|
302
197
|
Aberlaas is the base camp from which all great expedition start in the _La Horde
|
|
@@ -310,4 +205,4 @@ For your convenience, `aberlass` and `aberlas` are added as aliases by default.
|
|
|
310
205
|
|
|
311
206
|
## Documentation
|
|
312
207
|
|
|
313
|
-
The complete documentation can be found on https://projects.pixelastic.com/aberlaas/
|
|
208
|
+
The complete documentation can be found on https://projects.pixelastic.com/aberlaas/
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import Gilmore from 'gilmore';
|
|
3
|
+
import { copy, error as firostError, isFile, move, read, write } from 'firost';
|
|
4
|
+
import { _ } from 'golgoth';
|
|
5
|
+
import helper from '../../helper.js';
|
|
6
|
+
import nodeConfig from '../../configs/node.cjs';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* This hold functions shared for both the monorepo and simple init scenarios
|
|
10
|
+
**/
|
|
11
|
+
export default {
|
|
12
|
+
/**
|
|
13
|
+
* Return name of the current project, as the name of the current directory
|
|
14
|
+
* @returns {string} Name of the project
|
|
15
|
+
**/
|
|
16
|
+
getProjectName() {
|
|
17
|
+
return path.basename(helper.hostRoot());
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Return the name of the current author based on the GitHub project owner
|
|
22
|
+
* @returns {string} Name of the author, or __placeholder__ if undefined
|
|
23
|
+
**/
|
|
24
|
+
async getProjectAuthor() {
|
|
25
|
+
const repo = this.__getRepo();
|
|
26
|
+
return (await repo.githubRepoOwner()) || '__placeholder__';
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Copy a config template to the host
|
|
31
|
+
* @param {string} source Path to source file, relative to aberlaas
|
|
32
|
+
* @param {string} destination Path to destination file, relative to the host
|
|
33
|
+
* @returns {boolean} False if can't copy file, true otherwise
|
|
34
|
+
**/
|
|
35
|
+
async copyToHost(source, destination) {
|
|
36
|
+
const absoluteSource = helper.aberlaasPath(source);
|
|
37
|
+
const absoluteDestination = helper.hostPath(destination);
|
|
38
|
+
|
|
39
|
+
// Source file does not exist
|
|
40
|
+
if (!(await isFile(absoluteSource))) {
|
|
41
|
+
throw firostError(
|
|
42
|
+
'ERROR_INIT_COPY_FILE',
|
|
43
|
+
`Unable to locate ${absoluteSource} file`,
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
// Destination file already exist
|
|
47
|
+
if (await isFile(absoluteDestination)) {
|
|
48
|
+
// Do nothing if content is already the same
|
|
49
|
+
const sourceContent = await read(absoluteSource);
|
|
50
|
+
const destinationContent = await read(absoluteDestination);
|
|
51
|
+
if (sourceContent === destinationContent) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Otherwise create a backup
|
|
56
|
+
const backupDestination = `${absoluteDestination}.backup`;
|
|
57
|
+
await move(absoluteDestination, backupDestination);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
await copy(absoluteSource, absoluteDestination);
|
|
61
|
+
|
|
62
|
+
return true;
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Add MIT license file
|
|
67
|
+
* @param {string} hostFilepath Path to the LICENSE file, relative to the host
|
|
68
|
+
**/
|
|
69
|
+
async addLicenseFile(hostFilepath) {
|
|
70
|
+
// Start by adding a template
|
|
71
|
+
await this.copyToHost('templates/LICENSE', hostFilepath);
|
|
72
|
+
|
|
73
|
+
// Replace placeholder with real value
|
|
74
|
+
const licensePath = helper.hostPath(hostFilepath);
|
|
75
|
+
const author = await this.getProjectAuthor();
|
|
76
|
+
const templateContent = await read(licensePath);
|
|
77
|
+
const actualContent = _.replace(templateContent, '{author}', author);
|
|
78
|
+
|
|
79
|
+
// Write it again
|
|
80
|
+
await write(actualContent, licensePath);
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Add CircleCI Config file
|
|
85
|
+
**/
|
|
86
|
+
async addCircleCIConfigFile() {
|
|
87
|
+
const configFilepath = helper.hostPath('./.circleci/config.yml');
|
|
88
|
+
|
|
89
|
+
// Start by adding a template
|
|
90
|
+
await this.copyToHost('templates/_circleci/config.yml', configFilepath);
|
|
91
|
+
|
|
92
|
+
// Replace placeholder with real value
|
|
93
|
+
const templateContent = await read(configFilepath);
|
|
94
|
+
const actualContent = _.chain(templateContent)
|
|
95
|
+
.replace('{nodeVersion}', nodeConfig.nodeVersion)
|
|
96
|
+
.replace('{yarnVersion}', nodeConfig.yarnVersion)
|
|
97
|
+
.value();
|
|
98
|
+
|
|
99
|
+
// Write it again
|
|
100
|
+
await write(actualContent, configFilepath);
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Add default script files
|
|
105
|
+
**/
|
|
106
|
+
async addScripts() {
|
|
107
|
+
// Common
|
|
108
|
+
await this.copyToHost('templates/scripts/ci', 'scripts/ci');
|
|
109
|
+
await this.copyToHost('templates/scripts/compress', 'scripts/compress');
|
|
110
|
+
await this.copyToHost('templates/scripts/lint', 'scripts/lint');
|
|
111
|
+
await this.copyToHost('templates/scripts/lint-fix', 'scripts/lint-fix');
|
|
112
|
+
|
|
113
|
+
// Hooks
|
|
114
|
+
await this.copyToHost(
|
|
115
|
+
'./templates/scripts/hooks/pre-commit',
|
|
116
|
+
'./scripts/hooks/pre-commit',
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
// Lib
|
|
120
|
+
await this.copyToHost(
|
|
121
|
+
'templates/scripts/lib/release',
|
|
122
|
+
'scripts/lib/release',
|
|
123
|
+
);
|
|
124
|
+
await this.copyToHost('templates/scripts/lib/test', 'scripts/lib/test');
|
|
125
|
+
await this.copyToHost(
|
|
126
|
+
'templates/scripts/lib/test-watch',
|
|
127
|
+
'scripts/lib/test-watch',
|
|
128
|
+
);
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Add config files to the host. Each config files reference the default
|
|
133
|
+
* aberlaas config for its tool. This pattern allow end-users to use aberlaas
|
|
134
|
+
* default rules and overwrite them as they see fit
|
|
135
|
+
**/
|
|
136
|
+
async addConfigFiles() {
|
|
137
|
+
// Git
|
|
138
|
+
await this.copyToHost('./templates/_gitignore', './.gitignore');
|
|
139
|
+
await this.copyToHost('./templates/_gitattributes', './.gitattributes');
|
|
140
|
+
|
|
141
|
+
// Yarn
|
|
142
|
+
await this.copyToHost('templates/_yarnrc.yml', '.yarnrc.yml');
|
|
143
|
+
|
|
144
|
+
// ESLint
|
|
145
|
+
await this.copyToHost('templates/_eslintrc.cjs', '.eslintrc.cjs');
|
|
146
|
+
await this.copyToHost('templates/_eslintignore.conf', '.eslintignore');
|
|
147
|
+
|
|
148
|
+
// Lint-staged
|
|
149
|
+
await this.copyToHost(
|
|
150
|
+
'templates/lintstaged.config.js',
|
|
151
|
+
'lintstaged.config.js',
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
// Vite
|
|
155
|
+
await this.copyToHost('templates/vite.config.js', 'vite.config.js');
|
|
156
|
+
|
|
157
|
+
// Prettier
|
|
158
|
+
await this.copyToHost('templates/prettier.config.js', 'prettier.config.js');
|
|
159
|
+
|
|
160
|
+
// Stylelint
|
|
161
|
+
await this.copyToHost(
|
|
162
|
+
'templates/stylelint.config.js',
|
|
163
|
+
'stylelint.config.js',
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
// Renovate
|
|
167
|
+
await this.copyToHost(
|
|
168
|
+
'templates/_github/renovate.json',
|
|
169
|
+
'.github/renovate.json',
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
// CircleCI
|
|
173
|
+
await this.copyToHost(
|
|
174
|
+
'templates/_circleci/config.yml',
|
|
175
|
+
'.circleci/config.yml',
|
|
176
|
+
);
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Add default files required to have the minimum lib module
|
|
181
|
+
**/
|
|
182
|
+
async addLibFiles() {
|
|
183
|
+
await this.copyToHost('templates/lib/main.js', 'lib/main.js');
|
|
184
|
+
await this.copyToHost(
|
|
185
|
+
'templates/lib/__tests__/main.js',
|
|
186
|
+
'lib/__tests__/main.js',
|
|
187
|
+
);
|
|
188
|
+
},
|
|
189
|
+
__getRepo() {
|
|
190
|
+
return new Gilmore(helper.hostRoot());
|
|
191
|
+
},
|
|
192
|
+
};
|