pob 14.0.1 → 14.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/CHANGELOG.md CHANGED
@@ -3,6 +3,39 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [14.2.0](https://github.com/christophehurpeau/pob/compare/pob@14.1.0...pob@14.2.0) (2023-09-30)
7
+
8
+
9
+ ### Features
10
+
11
+ * improve monorepo detection ([c7e43ef](https://github.com/christophehurpeau/pob/commit/c7e43efd767e1cc7d786c22f47796fc5106be84b))
12
+ * **pob:** split yarn workspaces and legacy lerna ; use yarn to build graph ([4f3f77a](https://github.com/christophehurpeau/pob/commit/4f3f77a60a4e755815a836f76d53624573edcff4))
13
+
14
+
15
+ ### Bug Fixes
16
+
17
+ * **deps:** update @pob/eslint-config to v51.0.2 ([#1718](https://github.com/christophehurpeau/pob/issues/1718)) ([18a8330](https://github.com/christophehurpeau/pob/commit/18a8330c54729273d091cfbe49e387916fbde3ba))
18
+ * **deps:** update dependency yeoman-environment to v4.0.0-beta.5 ([#1785](https://github.com/christophehurpeau/pob/issues/1785)) ([deb0019](https://github.com/christophehurpeau/pob/commit/deb00197a177fdddb1037538969351ed8f2e1925))
19
+ * **deps:** update dependency yeoman-generator to v6.0.0-rc.5 ([#1786](https://github.com/christophehurpeau/pob/issues/1786)) ([6ebbde9](https://github.com/christophehurpeau/pob/commit/6ebbde9db265b44e8807fd4a83526b49984e5d33))
20
+
21
+
22
+
23
+ ## [14.1.0](https://github.com/christophehurpeau/pob/compare/pob@14.0.1...pob@14.1.0) (2023-09-24)
24
+
25
+
26
+ ### Features
27
+
28
+ * add option to disable yarn cache ([761436f](https://github.com/christophehurpeau/pob/commit/761436fb7946c52a4d02b61b5c81ea122d64dc05))
29
+ * **deps:** update dependency eslint to v8.50.0 ([#1778](https://github.com/christophehurpeau/pob/issues/1778)) ([9002346](https://github.com/christophehurpeau/pob/commit/9002346e6f50bf455a47e63141d2f4fdf8689f8e))
30
+ * update actions/checkout ([f040a5d](https://github.com/christophehurpeau/pob/commit/f040a5d3a87d8e3f5b5ae890ec49db37e435d22b))
31
+
32
+
33
+ ### Bug Fixes
34
+
35
+ * add missing options to disable yarn cache ([2138584](https://github.com/christophehurpeau/pob/commit/21385841d31755f44a5661a282d094dc994f7404))
36
+
37
+
38
+
6
39
  ## [14.0.1](https://github.com/christophehurpeau/pob/compare/pob@14.0.0...pob@14.0.1) (2023-09-18)
7
40
 
8
41
 
@@ -44,6 +44,13 @@ export default class PobAppGenerator extends Generator {
44
44
  default: 'node-modules',
45
45
  desc: 'Defines what linker should be used for installing Node packages (useful to enable the node-modules plugin), one of: pnp, node-modules.',
46
46
  });
47
+
48
+ this.option('disableYarnGitCache', {
49
+ type: Boolean,
50
+ required: false,
51
+ default: false,
52
+ desc: 'Disable git cache. See https://yarnpkg.com/features/caching#offline-mirror.',
53
+ });
47
54
  }
48
55
 
49
56
  initializing() {
@@ -205,6 +212,7 @@ export default class PobAppGenerator extends Generator {
205
212
  if (this.appConfig.type !== 'remix') {
206
213
  this.composeWith('pob:common:testing', {
207
214
  enable: this.appConfig.testing,
215
+ disableYarnGitCache: this.options.disableYarnGitCache,
208
216
  enableReleasePlease,
209
217
  testing: this.appConfig.testing,
210
218
  typescript: babel,
@@ -100,6 +100,13 @@ export default class CommonTestingGenerator extends Generator {
100
100
  default: 'src',
101
101
  desc: 'customize srcDir, if different than rootDir',
102
102
  });
103
+
104
+ this.option('disableYarnGitCache', {
105
+ type: Boolean,
106
+ required: false,
107
+ default: false,
108
+ desc: 'Disable git cache. See https://yarnpkg.com/features/caching#offline-mirror.',
109
+ });
103
110
  }
104
111
 
105
112
  default() {
@@ -108,6 +115,7 @@ export default class CommonTestingGenerator extends Generator {
108
115
  enable: this.options.ci,
109
116
  enableReleasePlease: this.options.enableReleasePlease,
110
117
  enableYarnVersion: this.options.enableYarnVersion,
118
+ disableYarnGitCache: this.options.disableYarnGitCache,
111
119
  testing: this.options.enable,
112
120
  build: this.options.build,
113
121
  typescript: this.options.typescript,
@@ -87,6 +87,13 @@ export default class CoreCIGenerator extends Generator {
87
87
  required: true,
88
88
  desc: 'split CI jobs for faster result',
89
89
  });
90
+
91
+ this.option('disableYarnGitCache', {
92
+ type: Boolean,
93
+ required: false,
94
+ default: false,
95
+ desc: 'Disable git cache. See https://yarnpkg.com/features/caching#offline-mirror.',
96
+ });
90
97
  }
91
98
 
92
99
  async prompting() {
@@ -136,6 +143,7 @@ export default class CoreCIGenerator extends Generator {
136
143
  this.destinationPath('.github/workflows/push.yml'),
137
144
  {
138
145
  packageManager: this.options.packageManager,
146
+ disableYarnGitCache: this.options.disableYarnGitCache,
139
147
  testing,
140
148
  checks,
141
149
  documentation: this.options.documentation,
@@ -184,6 +192,7 @@ export default class CoreCIGenerator extends Generator {
184
192
  this.destinationPath('.github/workflows/gh-pages.yml'),
185
193
  {
186
194
  packageManager: this.options.packageManager,
195
+ disableYarnGitCache: this.options.disableYarnGitCache,
187
196
  testing: this.options.testing,
188
197
  typedoc: this.options.documentation && this.options.typescript,
189
198
  },
@@ -8,14 +8,18 @@ jobs:
8
8
  create-documentation-and-deploy:
9
9
  runs-on: ubuntu-latest
10
10
  steps:
11
- - uses: actions/checkout@v3
11
+ - uses: actions/checkout@v4
12
12
 
13
13
  - uses: actions/setup-node@v3
14
14
  with:
15
15
  node-version: 18
16
16
 
17
17
  - name: Install Dependencies
18
+ <% if (disableYarnGitCache) { -%>
19
+ run: yarn install --immutable
20
+ <% } else { -%>
18
21
  run: yarn install --immutable --immutable-cache
22
+ <% } -%>
19
23
 
20
24
  <% if (typedoc) { -%>
21
25
  - name: Run tsc for tsc cache
@@ -8,14 +8,18 @@ jobs:
8
8
  runs-on: ubuntu-latest
9
9
 
10
10
  steps:
11
- - uses: actions/checkout@v3
11
+ - uses: actions/checkout@v4
12
12
 
13
13
  - uses: actions/setup-node@v3
14
14
  with:
15
15
  node-version: 18
16
16
 
17
17
  - name: Install Dependencies
18
+ <% if (disableYarnGitCache) { -%>
19
+ run: yarn install --immutable
20
+ <% } else { -%>
18
21
  run: yarn install --immutable --immutable-cache
22
+ <% } -%>
19
23
 
20
24
  - name: Checks
21
25
  run: yarn run checks
@@ -26,14 +30,18 @@ jobs:
26
30
  runs-on: ubuntu-latest
27
31
 
28
32
  steps:
29
- - uses: actions/checkout@v3
33
+ - uses: actions/checkout@v4
30
34
 
31
35
  - uses: actions/setup-node@v3
32
36
  with:
33
37
  node-version: 18
34
38
 
35
39
  - name: Install Dependencies
40
+ <% if (disableYarnGitCache) { -%>
41
+ run: yarn install --immutable
42
+ <% } else { -%>
36
43
  run: yarn install --immutable --immutable-cache
44
+ <% } -%>
37
45
 
38
46
  - name: Build
39
47
  run: yarn run build
@@ -46,14 +54,18 @@ jobs:
46
54
  runs-on: ubuntu-latest
47
55
 
48
56
  steps:
49
- - uses: actions/checkout@v3
57
+ - uses: actions/checkout@v4
50
58
 
51
59
  - uses: actions/setup-node@v3
52
60
  with:
53
61
  node-version: 18
54
62
 
55
63
  - name: Install Dependencies
64
+ <% if (disableYarnGitCache) { -%>
65
+ run: yarn install --immutable
66
+ <% } else { -%>
56
67
  run: yarn install --immutable --immutable-cache
68
+ <% } -%>
57
69
 
58
70
  - name: Prettier
59
71
  run: yarn run lint:prettier
@@ -75,14 +87,18 @@ jobs:
75
87
  node-version: [18, 20]
76
88
 
77
89
  steps:
78
- - uses: actions/checkout@v3
90
+ - uses: actions/checkout@v4
79
91
 
80
92
  - uses: actions/setup-node@v3
81
93
  with:
82
94
  node-version: ${{ matrix.node-version }}
83
95
 
84
96
  - name: Install Dependencies
97
+ <% if (disableYarnGitCache) { -%>
98
+ run: yarn install --immutable
99
+ <% } else { -%>
85
100
  run: yarn install --immutable --immutable-cache
101
+ <% } -%>
86
102
 
87
103
  <% if (codecov) { -%>
88
104
  - name: Test
@@ -111,7 +127,7 @@ jobs:
111
127
  needs: [<%= checks ? '"checks", ' : '' -%><%= build ? '"build", ' : '' -%>"lint"<%= testing ? ', "test"' : '' -%>]
112
128
 
113
129
  steps:
114
- - uses: actions/checkout@v3
130
+ - uses: actions/checkout@v4
115
131
 
116
132
  - uses: actions/setup-node@v3
117
133
  with:
@@ -11,7 +11,7 @@ jobs:
11
11
  node-version: [18.x<% if (!onlyLatestLTS) { -%>, 20.x <% } -%>]
12
12
 
13
13
  steps:
14
- - uses: actions/checkout@v3
14
+ - uses: actions/checkout@v4
15
15
 
16
16
  - name: Use Node.js ${{ matrix.node-version }}
17
17
  uses: actions/setup-node@v3
@@ -21,7 +21,11 @@ jobs:
21
21
 
22
22
  <% if (packageManager === 'yarn') { -%>
23
23
  - name: Install Dependencies
24
+ <% if (disableYarnGitCache) { -%>
25
+ run: yarn install --immutable
26
+ <% } else { -%>
24
27
  run: yarn install --immutable --immutable-cache
28
+ <% } -%>
25
29
  <% } else if (packageManager === 'npm') { -%>
26
30
  - name: Install Dependencies
27
31
  run: npm ci
@@ -42,7 +42,6 @@ export default class CoreGitGenerator extends Generator {
42
42
  originUrl &&
43
43
  typeof originUrl === 'string' &&
44
44
  originUrl.match(
45
- // eslint-disable-next-line unicorn/no-unsafe-regex
46
45
  /^(?:git@|https?:\/\/)(?:([^./:]+)(?:\.com)?[/:])?([^/:]+)\/([^./:]+)(?:.git)?/,
47
46
  );
48
47
  if (!match) return;
@@ -29,6 +29,13 @@ export default class CoreYarnGenerator extends Generator {
29
29
  default: 'node-modules',
30
30
  desc: 'Defines what linker should be used for installing Node packages (useful to enable the node-modules plugin), one of: pnp, node-modules.',
31
31
  });
32
+
33
+ this.option('disableYarnGitCache', {
34
+ type: Boolean,
35
+ required: false,
36
+ default: false,
37
+ desc: 'Disable git cache. See https://yarnpkg.com/features/caching#offline-mirror.',
38
+ });
32
39
  }
33
40
 
34
41
  initializing() {
@@ -52,7 +59,9 @@ export default class CoreYarnGenerator extends Generator {
52
59
  this.fs.copyTpl(
53
60
  this.templatePath('yarn_gitignore.ejs'),
54
61
  this.destinationPath('.yarn/.gitignore'),
55
- {},
62
+ {
63
+ disableYarnGitCache: this.options.disableYarnGitCache,
64
+ },
56
65
  );
57
66
 
58
67
  const { stdout } = this.spawnSync(
@@ -1,2 +1,5 @@
1
1
  /unplugged
2
2
  /install-state.gz
3
+ <% if (disableYarnGitCache) { -%>
4
+ /cache
5
+ <% } -%>
@@ -32,6 +32,13 @@ export default class PobLibGenerator extends Generator {
32
32
  default: 'node-modules',
33
33
  desc: 'Defines what linker should be used for installing Node packages (useful to enable the node-modules plugin), one of: pnp, node-modules.',
34
34
  });
35
+
36
+ this.option('disableYarnGitCache', {
37
+ type: Boolean,
38
+ required: false,
39
+ default: false,
40
+ desc: 'Disable git cache. See https://yarnpkg.com/features/caching#offline-mirror.',
41
+ });
35
42
  }
36
43
 
37
44
  initializing() {
@@ -264,6 +271,7 @@ export default class PobLibGenerator extends Generator {
264
271
 
265
272
  this.composeWith('pob:common:testing', {
266
273
  enable: this.pobjson.testing,
274
+ disableYarnGitCache: this.options.disableYarnGitCache,
267
275
  enableReleasePlease,
268
276
  testing: this.pobjson.testing,
269
277
  runner: this.pobjson.testing
@@ -68,7 +68,6 @@ export default class LibReadmeGenerator extends Generator {
68
68
  const match =
69
69
  repository &&
70
70
  repository.match(
71
- // eslint-disable-next-line unicorn/no-unsafe-regex
72
71
  /^(?:git@|https?:\/\/)(?:([^./:]+)(?:\.com)?[/:])?([^/:]+)\/([^./:]+)(?:.git)?/,
73
72
  );
74
73
  const [, gitHost, gitAccount, gitName] = match || [];
@@ -1,13 +1,31 @@
1
1
  import { execSync } from 'node:child_process';
2
2
  import fs from 'node:fs';
3
- import path from 'node:path';
4
3
  import { platform } from 'node:process';
5
- import { PackageGraph } from '@lerna/package-graph';
6
- import { Project as LernaProject } from '@lerna/project';
4
+ import { getPluginConfiguration } from '@yarnpkg/cli';
5
+ import { Configuration, Project } from '@yarnpkg/core';
6
+ import { ppath } from '@yarnpkg/fslib';
7
+ import {
8
+ buildTopologicalOrderBatches,
9
+ buildDependenciesMaps,
10
+ getWorkspaceName,
11
+ } from 'yarn-workspace-utils';
7
12
  import Generator from 'yeoman-generator';
8
13
  import * as packageUtils from '../../utils/package.js';
9
14
  import { copyAndFormatTpl } from '../../utils/writeAndFormat.js';
10
15
 
16
+ export const createYarnProject = async () => {
17
+ const portablePath = ppath.cwd();
18
+
19
+ const configuration = await Configuration.find(
20
+ portablePath,
21
+ // eslint-disable-next-line unicorn/no-array-method-this-argument -- not an array
22
+ getPluginConfiguration(),
23
+ );
24
+ // eslint-disable-next-line unicorn/no-array-method-this-argument -- not an array
25
+ const { project } = await Project.find(configuration, portablePath);
26
+ return project;
27
+ };
28
+
11
29
  const getAppTypes = (configs) => {
12
30
  const appConfigs = configs.filter(
13
31
  (config) => config && config.project && config.project.type === 'app',
@@ -26,9 +44,9 @@ const hasDist = (packages, configs) =>
26
44
  (config, index) =>
27
45
  !!(config && config.project && config.project.type === 'lib') &&
28
46
  !!(
29
- packages[index].get('pob') &&
30
- packages[index].get('pob').babelEnvs &&
31
- packages[index].get('pob').babelEnvs.length > 0
47
+ packages[index].pob &&
48
+ packages[index].pob.babelEnvs &&
49
+ packages[index].pob.babelEnvs.length > 0
32
50
  ),
33
51
  );
34
52
 
@@ -79,46 +97,38 @@ export default class PobMonorepoGenerator extends Generator {
79
97
  required: true,
80
98
  desc: 'only latest lts',
81
99
  });
100
+
101
+ this.option('disableYarnGitCache', {
102
+ type: Boolean,
103
+ required: false,
104
+ default: false,
105
+ desc: 'Disable git cache. See https://yarnpkg.com/features/caching#offline-mirror.',
106
+ });
82
107
  }
83
108
 
84
109
  async initializing() {
85
- this.lernaProject = new LernaProject(this.destinationPath());
86
- const packages = await this.lernaProject.getPackages();
87
- const graph = new PackageGraph(packages);
88
- const [cyclePaths] = graph.partitionCycles();
89
-
90
- if (cyclePaths.size > 0) {
91
- const cycleMessage = [
92
- 'Dependency cycles detected, you should fix these!',
93
- ...cyclePaths.map((cycle) => cycle.join(' -> ')),
94
- ].join('\n');
95
-
96
- console.warn(cycleMessage);
97
- }
110
+ const yarnProject = await createYarnProject(this.destinationPath());
111
+ const batches = buildTopologicalOrderBatches(
112
+ yarnProject,
113
+ buildDependenciesMaps(yarnProject),
114
+ );
98
115
 
99
116
  this.packages = [];
100
117
  this.packageLocations = [];
101
118
 
102
- while (graph.size > 0) {
103
- // pick the current set of nodes _without_ localDependencies (aka it is a "source" node)
104
- const batch = [...graph.values()].filter(
105
- (node) => node.localDependencies.size === 0,
106
- );
107
- batch.sort((a, b) => a.name.localeCompare(b.name, 'en'));
108
-
109
- // batches are composed of Package instances, not PackageGraphNodes
110
- this.packages.push(...batch.map((node) => node.pkg));
111
- this.packageLocations.push(
112
- ...batch.map((node) =>
113
- path
114
- .relative(this.destinationPath(), node.location)
115
- // transform windows path to linux
116
- .replace(/\\+/g, '/'),
117
- ),
119
+ for (const batch of batches) {
120
+ // sort by name to ensure consistent ordering
121
+ batch.sort((a, b) =>
122
+ getWorkspaceName(a).localeCompare(getWorkspaceName(b), 'en'),
118
123
  );
119
124
 
120
- // pruning the graph changes the node.localDependencies.size test
121
- graph.prune(...batch);
125
+ batch.forEach((workspace) => {
126
+ if (workspace === yarnProject.topLevelWorkspace) {
127
+ return;
128
+ }
129
+ this.packages.push(workspace.manifest.raw);
130
+ this.packageLocations.push(workspace.relativeCwd.toString());
131
+ });
122
132
  }
123
133
 
124
134
  this.packageNames = this.packages.map((pkg) => pkg.name);
@@ -218,6 +228,7 @@ export default class PobMonorepoGenerator extends Generator {
218
228
  this.composeWith('pob:common:testing', {
219
229
  monorepo: true,
220
230
  enable: this.pobLernaConfig.testing,
231
+ disableYarnGitCache: this.options.disableYarnGitCache,
221
232
  enableReleasePlease: false,
222
233
  enableYarnVersion: isYarnVersionEnabled,
223
234
  testing: this.pobLernaConfig.testing,
@@ -1,11 +1,7 @@
1
- import { spawnSync } from 'node:child_process';
2
1
  import { readdirSync, existsSync } from 'node:fs';
3
2
  import Generator from 'yeoman-generator';
4
3
  import * as packageUtils from '../../../utils/package.js';
5
- import {
6
- copyAndFormatTpl,
7
- writeAndFormatJson,
8
- } from '../../../utils/writeAndFormat.js';
4
+ import { writeAndFormatJson } from '../../../utils/writeAndFormat.js';
9
5
 
10
6
  export default class MonorepoLernaGenerator extends Generator {
11
7
  constructor(args, opts) {
@@ -22,8 +18,16 @@ export default class MonorepoLernaGenerator extends Generator {
22
18
  default: 'yarn',
23
19
  desc: 'yarn or npm',
24
20
  });
21
+
22
+ this.option('disableYarnGitCache', {
23
+ type: Boolean,
24
+ required: false,
25
+ default: false,
26
+ desc: 'Disable git cache. See https://yarnpkg.com/features/caching#offline-mirror.',
27
+ });
25
28
  }
26
29
 
30
+ // TODO pass packages as options ?
27
31
  initializing() {
28
32
  const pkg = this.fs.readJSON(this.destinationPath('package.json'), {});
29
33
  const packagesPaths = pkg.workspaces
@@ -121,65 +125,16 @@ export default class MonorepoLernaGenerator extends Generator {
121
125
  // package.json
122
126
  const pkg = this.fs.readJSON(this.destinationPath('package.json'), {});
123
127
  delete pkg.lerna;
124
- packageUtils.removeDependencies(pkg, ['lerna', '@pob/lerna-light']);
128
+ packageUtils.removeDependencies(pkg, ['lerna']);
125
129
  packageUtils.removeDevDependencies(pkg, ['lerna']);
126
130
 
127
131
  if (this.fs.exists(this.destinationPath('lerna-debug.log'))) {
128
132
  this.fs.delete(this.destinationPath('lerna-debug.log'));
129
133
  }
130
- if (this.npm) {
131
- if (!pkg.engines) pkg.engines = {};
132
- pkg.engines.yarn = '< 0.0.0';
133
- pkg.engines.npm = '>= 6.4.0';
134
- } else if (pkg.engines) {
135
- delete pkg.engines.yarn;
136
- }
137
-
138
- if (pkg.name === 'pob-monorepo') {
139
- pkg.devDependencies['@pob/lerna-light'] = 'workspace:*';
140
- } else {
141
- packageUtils.addOrRemoveDevDependencies(
142
- pkg,
143
- pkg.name !== 'pob-monorepo-test-repository' &&
144
- pkg.name !== 'use-react-intl-formatters-monorepo',
145
- // pkg.name !== '@pob/eslint-config-monorepo'
146
- ['@pob/lerna-light'],
147
- );
148
- }
149
134
 
150
135
  // TODO remove lerna completely
151
136
  const isYarnVersionEnabled = !pkg.devDependencies?.['@pob/lerna-light'];
152
137
 
153
- if (pkg.name !== 'pob-monorepo') {
154
- packageUtils.addDevDependencies(pkg, ['repository-check-dirty']);
155
- }
156
-
157
- packageUtils.removeDevDependencies(pkg, ['standard-version']);
158
-
159
- const monorepoConfig = this.config.get('monorepo');
160
- const packageManager = this.npm ? 'npm' : 'yarn';
161
-
162
- packageUtils.addScripts(pkg, {
163
- lint: `${packageManager} run lint:prettier && ${packageManager} run lint:eslint`,
164
- 'lint:prettier': 'pob-root-prettier --check .',
165
- 'lint:prettier:fix': 'pob-root-prettier --write .',
166
- 'lint:eslint':
167
- monorepoConfig &&
168
- monorepoConfig.eslint &&
169
- this.packagesConfig.length < 50
170
- ? `${
171
- this.packagesConfig.length > 15
172
- ? 'NODE_OPTIONS=--max_old_space_size=4096 '
173
- : ''
174
- }eslint --report-unused-disable-directives --resolve-plugins-relative-to . --quiet .`
175
- : // eslint-disable-next-line unicorn/no-nested-ternary
176
- this.options.packageManager === 'yarn'
177
- ? `NODE_OPTIONS=--max_old_space_size=4096 eslint --report-unused-disable-directives --resolve-plugins-relative-to . --quiet . --ignore-pattern ${pkg.workspaces.join(
178
- ',',
179
- )} && yarn workspaces foreach --parallel -Av run lint:eslint`
180
- : 'npm run lint:eslint --workspaces',
181
- });
182
-
183
138
  packageUtils.addOrRemoveScripts(
184
139
  pkg,
185
140
  this.options.packageManager === 'yarn' && !isYarnVersionEnabled,
@@ -189,112 +144,6 @@ export default class MonorepoLernaGenerator extends Generator {
189
144
  },
190
145
  );
191
146
 
192
- // TODO rename release (release = version + publish)
193
- this.fs.copyTpl(
194
- this.templatePath('workflow-publish.yml.ejs'),
195
- this.destinationPath('.github/workflows/publish.yml'),
196
- {
197
- publish: !this.options.isAppProject,
198
- enableYarnVersion: isYarnVersionEnabled,
199
- isIndependent: lernaConfig.version === 'independent',
200
- },
201
- );
202
-
203
- packageUtils.removeScripts(
204
- pkg,
205
- [pkg.name !== 'pob-dependencies' && 'preversion', 'release'].filter(
206
- Boolean,
207
- ),
208
- );
209
-
210
- packageUtils.addOrRemoveScripts(pkg, withBabel, {
211
- build:
212
- 'yarn workspaces foreach --parallel --topological-dev -Av run build',
213
- watch:
214
- 'yarn workspaces foreach --parallel --jobs unlimited --interlaced --exclude "*-example" -Av run watch',
215
- });
216
-
217
- // packageUtils.addOrRemoveScripts(pkg, withTypescript, {
218
- // 'build:definitions': `${
219
- // useYarnWorkspacesCommand
220
- // ? 'yarn workspaces foreach --parallel --exclude "*-example" -Av run'
221
- // : 'lerna run --stream'
222
- // } build:definitions`,
223
- // });
224
-
225
- // if (withTypescript) {
226
- // pkg.scripts.build += `${packageManager} run build:definitions${
227
- // useYarnWorkspacesCommand ? '' : ' --since'
228
- // }`;
229
- // }
230
-
231
- delete pkg.scripts.postbuild;
232
- delete pkg.scripts.prepublishOnly;
233
-
234
- if (this.npm) {
235
- delete pkg.workspaces;
236
- packageUtils.addScripts(pkg, {
237
- postinstall: 'lerna link',
238
- });
239
- } else if (!pkg.workspaces) {
240
- pkg.workspaces = ['packages/*'];
241
- }
242
-
243
147
  this.fs.writeJSON(this.destinationPath('package.json'), pkg);
244
-
245
- // README.md
246
- const readmePath = this.destinationPath('README.md');
247
-
248
- let content = '';
249
-
250
- if (this.fs.exists(readmePath)) {
251
- const readmeFullContent = this.fs.read(readmePath);
252
- content = readmeFullContent.match(/^<h3[^#*]+([^]+)$/);
253
- if (!content) content = readmeFullContent.match(/^#[^#*]+([^]+)$/);
254
- content = content ? content[1].trim() : readmeFullContent;
255
- }
256
-
257
- copyAndFormatTpl(this.fs, this.templatePath('README.md.ejs'), readmePath, {
258
- projectName: pkg.name,
259
- description: pkg.description,
260
- packages: this.packages,
261
- ci: this.fs.exists(this.destinationPath('.github/workflows/push.yml')),
262
- content,
263
- });
264
- }
265
-
266
- end() {
267
- this.packagePaths.forEach((packagePath) => {
268
- if (
269
- !existsSync(`${packagePath}/.yo-rc.json`) &&
270
- !existsSync(`${packagePath}/.pob.json`)
271
- ) {
272
- return;
273
- }
274
- console.log(`=> update ${packagePath}`);
275
- spawnSync(
276
- process.argv[0],
277
- [
278
- process.argv[1],
279
- 'update',
280
- 'from-pob',
281
- this.options.force ? '--force' : undefined,
282
- ].filter(Boolean),
283
- {
284
- cwd: packagePath,
285
- stdio: 'inherit',
286
- },
287
- );
288
- });
289
-
290
- switch (this.options.packageManager) {
291
- case 'npm':
292
- this.spawnCommandSync('npm', ['install']);
293
- this.spawnCommandSync('npm', ['run', 'preversion']);
294
- break;
295
- case 'yarn':
296
- // see CoreYarnGenerator
297
- break;
298
- }
299
148
  }
300
149
  }
@@ -0,0 +1,236 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { readdirSync, existsSync } from 'node:fs';
3
+ import Generator from 'yeoman-generator';
4
+ import * as packageUtils from '../../../utils/package.js';
5
+ import { copyAndFormatTpl } from '../../../utils/writeAndFormat.js';
6
+
7
+ export default class MonorepoLernaGenerator extends Generator {
8
+ constructor(args, opts) {
9
+ super(args, opts);
10
+
11
+ this.option('isAppProject', {
12
+ type: Boolean,
13
+ default: true,
14
+ desc: 'is app project',
15
+ });
16
+
17
+ this.option('packageManager', {
18
+ type: String,
19
+ default: 'yarn',
20
+ desc: 'yarn or npm',
21
+ });
22
+
23
+ this.option('disableYarnGitCache', {
24
+ type: Boolean,
25
+ required: false,
26
+ default: false,
27
+ desc: 'Disable git cache. See https://yarnpkg.com/features/caching#offline-mirror.',
28
+ });
29
+ }
30
+
31
+ initializing() {
32
+ const pkg = this.fs.readJSON(this.destinationPath('package.json'), {});
33
+ const packagesPaths = pkg.workspaces
34
+ ? pkg.workspaces.map((workspace) => workspace.replace(/\/\*$/, ''))
35
+ : ['packages'];
36
+
37
+ this.packagePaths = packagesPaths.flatMap((packagesPath) =>
38
+ existsSync(`${packagesPath}/`)
39
+ ? readdirSync(`${packagesPath}/`).map(
40
+ (packageName) => `${packagesPath}/${packageName}`,
41
+ )
42
+ : [],
43
+ );
44
+ this.packages = this.packagePaths
45
+ .map((packagePath) =>
46
+ this.fs.readJSON(this.destinationPath(`${packagePath}/package.json`)),
47
+ )
48
+ .filter(Boolean);
49
+ this.packagesConfig = this.packagePaths
50
+ .map((packagePath) =>
51
+ this.fs.readJSON(this.destinationPath(`${packagePath}/.yo-rc.json`)),
52
+ )
53
+ .filter(Boolean);
54
+ }
55
+
56
+ default() {}
57
+
58
+ writing() {
59
+ const getPackagePobConfig = (config) => ({
60
+ babelEnvs: [],
61
+ ...(config && config.pob),
62
+ });
63
+ const withBabel = this.packages.some(
64
+ (config) => getPackagePobConfig(config).babelEnvs.length > 0,
65
+ );
66
+
67
+ // package.json
68
+ const pkg = this.fs.readJSON(this.destinationPath('package.json'), {});
69
+ packageUtils.removeDependencies(pkg, ['@pob/lerna-light']);
70
+
71
+ if (this.npm) {
72
+ if (!pkg.engines) pkg.engines = {};
73
+ pkg.engines.yarn = '< 0.0.0';
74
+ pkg.engines.npm = '>= 6.4.0';
75
+ } else if (pkg.engines) {
76
+ delete pkg.engines.yarn;
77
+ }
78
+
79
+ if (pkg.name === 'pob-monorepo') {
80
+ pkg.devDependencies['@pob/lerna-light'] = 'workspace:*';
81
+ } else {
82
+ packageUtils.addOrRemoveDevDependencies(
83
+ pkg,
84
+ pkg.name !== 'pob-monorepo-test-repository' &&
85
+ pkg.name !== 'use-react-intl-formatters-monorepo',
86
+ // pkg.name !== '@pob/eslint-config-monorepo'
87
+ ['@pob/lerna-light'],
88
+ );
89
+ }
90
+
91
+ const isYarnVersionEnabled = !pkg.devDependencies?.['@pob/lerna-light'];
92
+
93
+ if (pkg.name !== 'pob-monorepo') {
94
+ packageUtils.addDevDependencies(pkg, ['repository-check-dirty']);
95
+ }
96
+
97
+ packageUtils.removeDevDependencies(pkg, ['standard-version']);
98
+
99
+ const monorepoConfig = this.config.get('monorepo');
100
+ const packageManager = this.npm ? 'npm' : 'yarn';
101
+
102
+ packageUtils.addScripts(pkg, {
103
+ lint: `${packageManager} run lint:prettier && ${packageManager} run lint:eslint`,
104
+ 'lint:prettier': 'pob-root-prettier --check .',
105
+ 'lint:prettier:fix': 'pob-root-prettier --write .',
106
+ 'lint:eslint':
107
+ monorepoConfig &&
108
+ monorepoConfig.eslint &&
109
+ this.packagesConfig.length < 50
110
+ ? `${
111
+ this.packagesConfig.length > 15
112
+ ? 'NODE_OPTIONS=--max_old_space_size=4096 '
113
+ : ''
114
+ }eslint --report-unused-disable-directives --resolve-plugins-relative-to . --quiet .`
115
+ : // eslint-disable-next-line unicorn/no-nested-ternary
116
+ this.options.packageManager === 'yarn'
117
+ ? `NODE_OPTIONS=--max_old_space_size=4096 eslint --report-unused-disable-directives --resolve-plugins-relative-to . --quiet . --ignore-pattern ${pkg.workspaces.join(
118
+ ',',
119
+ )} && yarn workspaces foreach --parallel -Av run lint:eslint`
120
+ : 'npm run lint:eslint --workspaces',
121
+ });
122
+
123
+ packageUtils.addOrRemoveScripts(
124
+ pkg,
125
+ this.options.packageManager === 'yarn' && !isYarnVersionEnabled,
126
+ {
127
+ version:
128
+ 'YARN_ENABLE_IMMUTABLE_INSTALLS=false yarn && git add yarn.lock',
129
+ },
130
+ );
131
+
132
+ // TODO rename release (release = version + publish)
133
+ this.fs.copyTpl(
134
+ this.templatePath('workflow-publish.yml.ejs'),
135
+ this.destinationPath('.github/workflows/publish.yml'),
136
+ {
137
+ publish: !this.options.isAppProject,
138
+ enableYarnVersion: isYarnVersionEnabled,
139
+ disableYarnGitCache: this.options.disableYarnGitCache,
140
+ isIndependent: !pkg.version || pkg.version === '0.0.0',
141
+ },
142
+ );
143
+
144
+ packageUtils.removeScripts(
145
+ pkg,
146
+ [pkg.name !== 'pob-dependencies' && 'preversion', 'release'].filter(
147
+ Boolean,
148
+ ),
149
+ );
150
+
151
+ packageUtils.addOrRemoveScripts(pkg, withBabel, {
152
+ build:
153
+ 'yarn workspaces foreach --parallel --topological-dev -Av run build',
154
+ watch:
155
+ 'yarn workspaces foreach --parallel --jobs unlimited --interlaced --exclude "*-example" -Av run watch',
156
+ });
157
+
158
+ // packageUtils.addOrRemoveScripts(pkg, withTypescript, {
159
+ // 'build:definitions': `${
160
+ // useYarnWorkspacesCommand
161
+ // ? 'yarn workspaces foreach --parallel --exclude "*-example" -Av run'
162
+ // : 'lerna run --stream'
163
+ // } build:definitions`,
164
+ // });
165
+
166
+ // if (withTypescript) {
167
+ // pkg.scripts.build += `${packageManager} run build:definitions${
168
+ // useYarnWorkspacesCommand ? '' : ' --since'
169
+ // }`;
170
+ // }
171
+
172
+ delete pkg.scripts.postbuild;
173
+ delete pkg.scripts.prepublishOnly;
174
+
175
+ if (!pkg.workspaces) {
176
+ pkg.workspaces = ['packages/*'];
177
+ }
178
+
179
+ this.fs.writeJSON(this.destinationPath('package.json'), pkg);
180
+
181
+ // README.md
182
+ const readmePath = this.destinationPath('README.md');
183
+
184
+ let content = '';
185
+
186
+ if (this.fs.exists(readmePath)) {
187
+ const readmeFullContent = this.fs.read(readmePath);
188
+ content = readmeFullContent.match(/^<h3[^#*]+([^]+)$/);
189
+ if (!content) content = readmeFullContent.match(/^#[^#*]+([^]+)$/);
190
+ content = content ? content[1].trim() : readmeFullContent;
191
+ }
192
+
193
+ copyAndFormatTpl(this.fs, this.templatePath('README.md.ejs'), readmePath, {
194
+ projectName: pkg.name,
195
+ description: pkg.description,
196
+ packages: this.packages,
197
+ ci: this.fs.exists(this.destinationPath('.github/workflows/push.yml')),
198
+ content,
199
+ });
200
+ }
201
+
202
+ end() {
203
+ this.packagePaths.forEach((packagePath) => {
204
+ if (
205
+ !existsSync(`${packagePath}/.yo-rc.json`) &&
206
+ !existsSync(`${packagePath}/.pob.json`)
207
+ ) {
208
+ return;
209
+ }
210
+ console.log(`=> update ${packagePath}`);
211
+ spawnSync(
212
+ process.argv[0],
213
+ [
214
+ process.argv[1],
215
+ 'update',
216
+ 'from-pob',
217
+ this.options.force ? '--force' : undefined,
218
+ ].filter(Boolean),
219
+ {
220
+ cwd: packagePath,
221
+ stdio: 'inherit',
222
+ },
223
+ );
224
+ });
225
+
226
+ switch (this.options.packageManager) {
227
+ case 'npm':
228
+ this.spawnCommandSync('npm', ['install']);
229
+ this.spawnCommandSync('npm', ['run', 'preversion']);
230
+ break;
231
+ case 'yarn':
232
+ // see CoreYarnGenerator
233
+ break;
234
+ }
235
+ }
236
+ }
@@ -10,7 +10,7 @@
10
10
 
11
11
  <h1>Packages</h1>
12
12
 
13
- This repository is a monorepo that we manage using [Lerna](https://github.com/lerna/lerna).
13
+ This repository is a monorepo that we manage using [Yarn Workspaces](https://yarnpkg.com/features/workspaces).
14
14
 
15
15
  | Package | Version | Description |
16
16
  |---------|---------|-------------|
@@ -23,7 +23,7 @@ jobs:
23
23
  publish:
24
24
  runs-on: ubuntu-latest
25
25
  steps:
26
- - uses: actions/checkout@v3
26
+ - uses: actions/checkout@v4
27
27
  with:
28
28
  token: ${{ secrets.GH_TOKEN }}
29
29
  fetch-depth: 0
@@ -33,7 +33,11 @@ jobs:
33
33
  node-version: 18
34
34
 
35
35
  - name: Install Dependencies
36
+ <% if (disableYarnGitCache) { -%>
37
+ run: yarn install --immutable
38
+ <% } else { -%>
36
39
  run: yarn install --immutable --immutable-cache
40
+ <% } -%>
37
41
 
38
42
  - name: New version (dry run)
39
43
  if: github.ref == 'refs/heads/main' && inputs.dry-run
@@ -125,6 +125,7 @@ export default class PobBaseGenerator extends Generator {
125
125
  type: this.projectConfig.type,
126
126
  enable: this.isRoot && this.projectConfig.packageManager === 'yarn',
127
127
  yarnNodeLinker: this.projectConfig.yarnNodeLinker,
128
+ disableYarnGitCache: this.projectConfig.disableYarnGitCache,
128
129
  });
129
130
 
130
131
  this.composeWith('pob:core:package', {
@@ -135,10 +136,17 @@ export default class PobBaseGenerator extends Generator {
135
136
  });
136
137
 
137
138
  if (this.useLerna) {
139
+ this.composeWith('pob:monorepo:workspaces', {
140
+ force: this.options.force,
141
+ isAppProject: this.projectConfig.type === 'app',
142
+ packageManager: this.projectConfig.packageManager,
143
+ disableYarnGitCache: this.projectConfig.disableYarnGitCache,
144
+ });
138
145
  this.composeWith('pob:monorepo:lerna', {
139
146
  force: this.options.force,
140
147
  isAppProject: this.projectConfig.type === 'app',
141
148
  packageManager: this.projectConfig.packageManager,
149
+ disableYarnGitCache: this.projectConfig.disableYarnGitCache,
142
150
  });
143
151
  }
144
152
 
@@ -197,6 +205,7 @@ export default class PobBaseGenerator extends Generator {
197
205
  ),
198
206
  {
199
207
  updateOnly: this.options.updateOnly,
208
+ disableYarnGitCache: this.projectConfig.disableYarnGitCache,
200
209
  isAppProject: this.projectConfig.type === 'app',
201
210
  packageManager: this.projectConfig.packageManager,
202
211
  yarnNodeLinker: this.projectConfig.yarnNodeLinker,
@@ -209,6 +218,7 @@ export default class PobBaseGenerator extends Generator {
209
218
  this.composeWith('pob:lib', {
210
219
  monorepo: this.useLerna,
211
220
  isRoot: this.isRoot,
221
+ disableYarnGitCache: this.projectConfig.disableYarnGitCache,
212
222
  updateOnly: this.options.updateOnly,
213
223
  fromPob: this.options.fromPob,
214
224
  packageManager: this.projectConfig.packageManager,
@@ -219,6 +229,7 @@ export default class PobBaseGenerator extends Generator {
219
229
  this.composeWith('pob:app', {
220
230
  monorepo: this.useLerna,
221
231
  isRoot: this.isRoot,
232
+ disableYarnGitCache: this.projectConfig.disableYarnGitCache,
222
233
  updateOnly: this.options.updateOnly,
223
234
  fromPob: this.options.fromPob,
224
235
  packageManager: this.projectConfig.packageManager,
package/lib/pob.js CHANGED
@@ -207,6 +207,11 @@ env.registerStub(
207
207
  'pob:lib:readme',
208
208
  `${__dirname}/generators/lib/readme/LibReadmeGenerator.js`,
209
209
  );
210
+ env.registerStub(
211
+ MonorepoLernaGenerator,
212
+ 'pob:monorepo:workspaces',
213
+ `${__dirname}/generators/monorepo/workspaces/MonorepoWorkspacesGenerator.js`,
214
+ );
210
215
  env.registerStub(
211
216
  MonorepoLernaGenerator,
212
217
  'pob:monorepo:lerna',
@@ -315,7 +320,10 @@ if (!existsSync('.yo-rc.json')) {
315
320
  writeFileSync('.yo-rc.json', '{}');
316
321
  }
317
322
 
318
- if (existsSync('lerna.json') || (projectPkg && projectPkg.lerna)) {
323
+ if (
324
+ existsSync('lerna.json') ||
325
+ (projectPkg && (projectPkg.lerna || projectPkg.workspaces))
326
+ ) {
319
327
  monorepo = true;
320
328
  }
321
329
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pob",
3
- "version": "14.0.1",
3
+ "version": "14.2.0",
4
4
  "description": "Pile of bones, library generator with git/babel/typescript/typedoc/readme/jest",
5
5
  "keywords": [
6
6
  "skeleton"
@@ -36,17 +36,18 @@
36
36
  "prettier": "@pob/root/prettier-config",
37
37
  "pob": {},
38
38
  "dependencies": {
39
- "@lerna/package-graph": "5.5.4",
40
- "@lerna/project": "5.5.4",
41
- "@pob/eslint-config": "51.0.0",
42
- "@pob/eslint-config-typescript": "51.0.0",
43
- "@pob/eslint-config-typescript-react": "51.0.0",
39
+ "@pob/eslint-config": "51.0.2",
40
+ "@pob/eslint-config-typescript": "51.0.2",
41
+ "@pob/eslint-config-typescript-react": "51.0.2",
44
42
  "@pob/sort-eslint-config": "5.0.1",
45
43
  "@pob/sort-object": "6.0.1",
46
44
  "@pob/sort-pkg": "6.0.1",
47
45
  "@types/inquirer": "9.0.3",
46
+ "@yarnpkg/cli": "3.6.3",
47
+ "@yarnpkg/core": "3.5.3",
48
+ "@yarnpkg/fslib": "2.10.3",
48
49
  "@yeoman/types": "1.0.1",
49
- "eslint": "8.49.0",
50
+ "eslint": "8.50.0",
50
51
  "findup-sync": "^5.0.0",
51
52
  "git-remote-url": "^1.0.1",
52
53
  "github-username": "^7.0.0",
@@ -59,13 +60,14 @@
59
60
  "mem-fs-editor": "10.0.2",
60
61
  "minimist": "1.2.8",
61
62
  "parse-author": "2.0.0",
62
- "pob-dependencies": "8.5.1",
63
+ "pob-dependencies": "8.6.1",
63
64
  "prettier": "2.8.8",
64
65
  "semver": "7.5.4",
65
- "yeoman-environment": "4.0.0-beta.4",
66
- "yeoman-generator": "6.0.0-rc.4"
66
+ "yarn-workspace-utils": "1.0.0",
67
+ "yeoman-environment": "4.0.0-beta.5",
68
+ "yeoman-generator": "6.0.0-rc.5"
67
69
  },
68
70
  "devDependencies": {
69
- "@pob/root": "8.2.2"
71
+ "@pob/root": "8.3.1"
70
72
  }
71
73
  }