wp-blank-scripts 3.1.18 → 4.0.0-alpha.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.
@@ -0,0 +1,86 @@
1
+ const { execSync } = require('child_process');
2
+ const path = require('path');
3
+
4
+ const logger = require('../logger');
5
+ const getProjectPath = require('../utils/getProjectPath');
6
+
7
+ // Migrations in recommended order. Division must run before module because
8
+ // the module migrator rewrites files and division patterns are easier to
9
+ // detect in the original @import-based code.
10
+ const MIGRATIONS = ['division', 'color', 'module'];
11
+
12
+ const MIGRATION_NOTES = {
13
+ division: 'Replaces `/` division with `math.div()` and adds `@use "sass:math"` where needed.',
14
+ color:
15
+ 'Replaces deprecated color functions (darken, lighten, mix, etc.) with color.adjust()/color.scale().',
16
+ module:
17
+ 'Converts @import to @use/@forward. This is the most complex migration — review all changes carefully. ' +
18
+ 'Variables/mixins/functions will be namespaced (e.g. variables.$var instead of $var).',
19
+ };
20
+
21
+ module.exports = async (argv) => {
22
+ const migration = argv.migration;
23
+ const scssGlob = argv.glob || 'src/**/*.scss';
24
+ const dryRun = argv['dry-run'];
25
+
26
+ if (migration && !MIGRATIONS.includes(migration)) {
27
+ logger.error(`Unknown migration "${migration}". Valid options: ${MIGRATIONS.join(', ')}`);
28
+ process.exit(1);
29
+ }
30
+
31
+ const migrationsToRun = migration ? [migration] : MIGRATIONS;
32
+
33
+ logger.log('\nSass migration tool — wraps the official sass-migrator.\n');
34
+
35
+ if (dryRun) {
36
+ logger.log('Dry run mode — no files will be changed.\n');
37
+ }
38
+
39
+ for (const m of migrationsToRun) {
40
+ logger.log(`→ ${m}: ${MIGRATION_NOTES[m]}\n`);
41
+
42
+ const flags = [
43
+ m,
44
+ dryRun ? '--dry-run' : '',
45
+ // Allow sass-migrator to resolve node_modules imports the same way webpack does
46
+ '--load-path=.',
47
+ // For the module migration, also migrate imported dependencies
48
+ m === 'module' ? '--migrate-deps' : '',
49
+ scssGlob,
50
+ ]
51
+ .filter(Boolean)
52
+ .join(' ');
53
+
54
+ const cmd = `npx sass-migrator ${flags}`;
55
+
56
+ logger.log(`Running: ${cmd}\n`);
57
+
58
+ try {
59
+ execSync(cmd, {
60
+ cwd: getProjectPath(),
61
+ stdio: 'inherit',
62
+ });
63
+ logger.success(`${m} migration complete\n`);
64
+ } catch (err) {
65
+ logger.error(`${m} migration failed — fix errors above before continuing.\n`);
66
+ process.exit(1);
67
+ }
68
+
69
+ if (m !== migrationsToRun[migrationsToRun.length - 1]) {
70
+ logger.log('────────────────────────────────────\n');
71
+ }
72
+ }
73
+
74
+ if (!dryRun) {
75
+ logger.success(
76
+ 'All migrations complete. Run your build to verify output, then commit the changes.'
77
+ );
78
+
79
+ if (migrationsToRun.includes('module')) {
80
+ logger.log(
81
+ '\nNote: The module migration renames namespaces. Check any partials that were ' +
82
+ 'imported multiple times — they may need @forward in an index file.'
83
+ );
84
+ }
85
+ }
86
+ };
package/cli/setup.js CHANGED
@@ -8,44 +8,38 @@ const setupProject = require('../lib/setupProject');
8
8
  const logger = require('../logger');
9
9
  const getCliOptions = require('../utils/getCliOptions');
10
10
  const getMAMPDocumentRoot = require('../utils/getMAMPDocumentRoot');
11
- const getDefaultSetup = require('../utils/getDefaultSetup');
12
11
  const getProjectPath = require('../utils/getProjectPath');
13
12
  const exitWithError = require('../utils/exitWithError');
14
13
 
14
+ const AGENCY = 'ViVO Digital';
15
+ const AGENCY_SLUG = 'vivo';
16
+ const AGENCY_WEBSITE = 'https://vivo.digital';
17
+
15
18
  const documentRoot = getMAMPDocumentRoot();
16
- const defaultAnswers = getDefaultSetup();
17
19
 
18
20
  async function confirmOptions(options) {
19
- const {
20
- database,
21
- project,
22
- projectLocation,
23
- projectSlug,
24
- agency,
25
- agencySlug,
26
- agencyWebsite,
27
- tablePrefix,
28
- productionDomain,
29
- } = options;
21
+ const { database, project, projectLocation, projectSlug, tablePrefix, productionDomain } =
22
+ options;
30
23
 
31
24
  const projectSettings = [
32
25
  ['Project Location', projectLocation],
33
26
  ['Project Slug', projectSlug],
34
27
  ['Project Name', project],
35
- ['Agency', agency],
36
- ['Agency Slug', agencySlug],
37
- ['Agency Website', agencyWebsite],
38
28
  ['Database', database],
39
29
  ['Database Table Prefix', tablePrefix],
40
30
  ['Production Domain', productionDomain],
41
31
  ];
42
32
 
33
+ logger.log(
34
+ `\nProject settings:\n${projectSettings
35
+ .map(([title, value]) => ` ${chalk.yellow(title)}: ${value || chalk.dim('(empty)')}`)
36
+ .join('\n')}\n`
37
+ );
38
+
43
39
  const res = await inquirer.prompt([
44
40
  {
45
41
  name: 'confirm',
46
- message: `Please confirm your project settings:\n${projectSettings
47
- .map(([title, value]) => `${chalk.yellow(title)}: ${value}`)
48
- .join('\n')}`,
42
+ message: 'Does this look correct?',
49
43
  type: 'confirm',
50
44
  default: false,
51
45
  },
@@ -66,50 +60,34 @@ async function selectOptions() {
66
60
  {
67
61
  name: 'projectSlug',
68
62
  message: 'What is the project slug (folder name)?',
69
- default: path
70
- .relative(documentRoot, getProjectPath())
71
- .split('/')
72
- .pop(),
63
+ default: path.relative(documentRoot, getProjectPath()).split('/').pop(),
73
64
  },
74
65
  {
75
66
  name: 'project',
76
67
  message: 'What is the project name?',
77
- default: defaultAnswers.project,
78
68
  },
79
69
  {
80
70
  name: 'productionDomain',
81
71
  message: 'What is the production domain? (example.com)',
82
72
  default: '',
83
73
  },
84
- {
85
- name: 'agency',
86
- message: "What's the name of the agency?",
87
- default: defaultAnswers.agency,
88
- },
89
- {
90
- name: 'agencySlug',
91
- message: "What's the agency slug?",
92
- default: defaultAnswers.agencySlug,
93
- },
94
- {
95
- name: 'agencyWebsite',
96
- message: 'Whats the agency website?',
97
- default: defaultAnswers.agencyWebsite,
98
- },
99
74
  {
100
75
  name: 'tablePrefix',
101
76
  message: 'Whats the database table prefix?',
102
- default: answers =>
103
- (answers.project || defaultAnswers.agencySlug)
77
+ default: (answers) =>
78
+ (answers.project || AGENCY_SLUG)
104
79
  .split(' ')
105
- .map(a => (a[0] || '').toLowerCase())
80
+ .map((a) => (a[0] || '').toLowerCase())
106
81
  .join(''),
107
82
  },
108
83
  ]);
109
84
 
110
85
  options = Object.assign({}, res, {
111
- // Create database name based on agency and project name
112
- database: slugify(`${res.agencySlug} ${res.project}`),
86
+ agency: AGENCY,
87
+ agencySlug: AGENCY_SLUG,
88
+ agencyWebsite: AGENCY_WEBSITE,
89
+ // Create database name based on agency slug and project name
90
+ database: slugify(`${AGENCY_SLUG} ${res.project}`),
113
91
  // Ensure table prefix ends with an underscore
114
92
  tablePrefix:
115
93
  res.tablePrefix[res.tablePrefix.length - 1] === '_' ? res.tablePrefix : `${res.tablePrefix}_`,
package/index.js CHANGED
@@ -11,6 +11,7 @@ const importSQL = require('./cli/import');
11
11
  const exportSQL = require('./cli/export');
12
12
  const pullSQL = require('./cli/pull');
13
13
  const addDeployConfig = require('./cli/config');
14
+ const migrateSass = require('./cli/migrate-sass');
14
15
 
15
16
  const exitWithError = require('./utils/exitWithError');
16
17
 
@@ -40,15 +41,6 @@ yargs
40
41
  .options('project', {
41
42
  describe: 'The project name.',
42
43
  })
43
- .options('agency', {
44
- describe: 'The name of the agency.',
45
- })
46
- .options('agencySlug', {
47
- describe: 'The slug version of the agency name.',
48
- })
49
- .options('agencyWebsite', {
50
- describe: 'The agency website url.',
51
- })
52
44
  .options('tablePrefix', {
53
45
  describe: 'The table prefix for wordpress tables.',
54
46
  }),
@@ -63,13 +55,13 @@ yargs
63
55
  describe: 'The environment to deploy to.',
64
56
  })
65
57
  .option('mode', {
66
- describe: 'What should be deployed, Site, wp-content, Theme or Sql Only.',
58
+ describe: 'What should be deployed, Site, wp-content, Theme or SQL Only.',
67
59
  })
68
60
  .option('sql', {
69
61
  describe: "Should SQL be deployed, this is assumed true if mode is 'SQL only'.",
70
62
  })
71
63
  .option('serverPassword', {
72
- describe: 'The password for the configured user on the server, if sql is true.',
64
+ describe: 'The password for the configured user on the server, if SQL is true.',
73
65
  })
74
66
  .option('serverPrivateKey', {
75
67
  describe: 'The path to private key efaults to ~/.ssh/id_rsa',
@@ -125,6 +117,25 @@ yargs
125
117
  }),
126
118
  hooks
127
119
  )
120
+ .command(
121
+ 'migrate-sass',
122
+ 'Migrate legacy SCSS to Dart Sass 3.0 compatible syntax',
123
+ (yargs) =>
124
+ yargs
125
+ .option('migration', {
126
+ describe: `Specific migration to run: ${['division', 'color', 'module'].join(
127
+ ', '
128
+ )}. Omit to run all in order.`,
129
+ })
130
+ .option('glob', {
131
+ describe: 'Glob pattern for SCSS files. Defaults to src/**/*.scss',
132
+ })
133
+ .option('dry-run', {
134
+ describe: 'Preview changes without writing files.',
135
+ type: 'boolean',
136
+ }),
137
+ migrateSass
138
+ )
128
139
  .command(
129
140
  'config',
130
141
  'Add deployment config',
@@ -8,6 +8,7 @@ const { rimraf } = require('rimraf');
8
8
 
9
9
  const backupProject = require('./backupProject');
10
10
  const isCI = require('../utils/isCI');
11
+ const ensureGitignore = require('../utils/ensureGitignore');
11
12
  const isReactProject = require('../utils/isReactProject');
12
13
  const exitWithError = require('../utils/exitWithError');
13
14
  const logger = require('../logger');
@@ -54,6 +55,17 @@ module.exports = async (envConfig = {}, buildOptions, failOnError = false) => {
54
55
 
55
56
  logger.info(`Environment: ${options.environment}`);
56
57
 
58
+ // For non-local builds, route output to a separate directory so the dev
59
+ // server's dist/ is never touched. DEPLOY_OUT_DIR takes precedence (set by
60
+ // the deploy command), otherwise derive from environment name.
61
+ if (options.environment !== 'local' && !process.env.DEPLOY_OUT_DIR) {
62
+ process.env.DEPLOY_OUT_DIR = `dist-${options.environment}`;
63
+ }
64
+
65
+ if (process.env.DEPLOY_OUT_DIR) {
66
+ ensureGitignore('dist-*');
67
+ }
68
+
57
69
  if (!isCI() && !process.env.PORT) {
58
70
  // Only try 8 ports (BrowserSync uses 2)
59
71
  const port = await getPort({
@@ -78,7 +90,7 @@ module.exports = async (envConfig = {}, buildOptions, failOnError = false) => {
78
90
  webpackConfig = webpackConfig();
79
91
  }
80
92
 
81
- rimraf('dist');
93
+ rimraf(process.env.DEPLOY_OUT_DIR || 'dist');
82
94
 
83
95
  const config = merge(...[baseConfig, envConfig, buildConfig, webpackConfig].filter(Boolean));
84
96
 
@@ -157,13 +169,13 @@ module.exports = async (envConfig = {}, buildOptions, failOnError = false) => {
157
169
  const info = stats.toJson();
158
170
 
159
171
  if (stats.hasWarnings()) {
160
- console.log(info.warnings);
172
+ logger.warn(info.warnings);
161
173
  info.warnings.forEach((warning) => logger.warn(warning));
162
174
  }
163
175
 
164
176
  if (stats.hasErrors()) {
165
177
  info.errors.forEach((error) => {
166
- console.log(error);
178
+ logger.error(error);
167
179
  });
168
180
 
169
181
  logger.error('Build finished with errors');
@@ -2,6 +2,7 @@ const fs = require('fs');
2
2
  const path = require('path');
3
3
  const mysql = require('mysql');
4
4
  const Rsync = require('rsync');
5
+ const { rimraf } = require('rimraf');
5
6
 
6
7
  const exportSql = require('./exportSQL');
7
8
  const cleanShellPath = require('../utils/cleanShellPath');
@@ -9,11 +10,9 @@ const tunnel = require('../utils/tunnelSsh');
9
10
  const git = require('../utils/git');
10
11
  const runProjectBuildTask = require('../utils/runProjectBuildTask');
11
12
  const bumpVersion = require('../utils/bumpVersion');
12
- const isCI = require('../utils/isCI');
13
13
  const execAsync = require('../utils/execAsync');
14
14
  const { DEPLOY_MODES } = require('../constants');
15
15
  const getProjectPath = require('../utils/getProjectPath');
16
- const deploySQLFromCI = require('../utils/deploySQLFromCI');
17
16
  const getSettings = require('../utils/projectSettings');
18
17
  const logger = require('../logger');
19
18
  const replaceSQL = require('../utils/replaceSQL');
@@ -32,9 +31,9 @@ let options;
32
31
  let settings;
33
32
  let deployModeOptions;
34
33
 
35
- function setDeployModeOptions() {
34
+ function setDeployModeOptions(deployBuildDir) {
36
35
  const project = settings.project;
37
- const siteDirectory = settings.directories.build;
36
+ const siteDirectory = deployBuildDir || settings.directories.build;
38
37
 
39
38
  deployModeOptions = {
40
39
  [DEPLOY_MODES.SITE]: {
@@ -69,12 +68,6 @@ async function createTunnel() {
69
68
 
70
69
  const { tunnelHost, tunnelPort, mySQLPort } = options;
71
70
 
72
- if (isCI()) {
73
- execAsync(`ssh -L ${tunnelPort}:${tunnelHost}:${mySQLPort} -N ${server.user}@${server.host}`);
74
- logger.log(`Created tunnel to ${server.host}`);
75
- return;
76
- }
77
-
78
71
  let serverPrivateKey;
79
72
  if (options.serverPrivateKey) {
80
73
  serverPrivateKey = fs.readFileSync(cleanShellPath(options.serverPrivateKey));
@@ -93,7 +86,7 @@ async function createTunnel() {
93
86
  };
94
87
 
95
88
  return new Promise((resolve, reject) => {
96
- t = tunnel(tunnelConfig, error => {
89
+ t = tunnel(tunnelConfig, (error) => {
97
90
  if (error) {
98
91
  reject(new Error(`Error connecting: ${error}`));
99
92
  return;
@@ -103,7 +96,7 @@ async function createTunnel() {
103
96
  resolve();
104
97
  });
105
98
 
106
- t.on('error', error => {
99
+ t.on('error', (error) => {
107
100
  reject(new Error(`${error}`));
108
101
  });
109
102
  });
@@ -166,7 +159,7 @@ async function deployToServer() {
166
159
  logger.log('Starting deployment');
167
160
 
168
161
  rsync.execute(
169
- error => {
162
+ (error) => {
170
163
  if (error) {
171
164
  reject(error);
172
165
  return;
@@ -175,7 +168,7 @@ async function deployToServer() {
175
168
  logger.success(`Successfully deployed ${mode}`);
176
169
  resolve();
177
170
  },
178
- data => {
171
+ (data) => {
179
172
  logger.log(`${data}`);
180
173
  }
181
174
  );
@@ -200,7 +193,7 @@ async function connectMySQL() {
200
193
  multipleStatements: true,
201
194
  });
202
195
 
203
- m.connect(err => {
196
+ m.connect((err) => {
204
197
  if (err) {
205
198
  reject(err);
206
199
  return;
@@ -222,13 +215,13 @@ async function deployMySQL(file) {
222
215
  logger.log('Starting SQL deployment');
223
216
 
224
217
  const sql = fs.readFileSync(file).toString();
225
- m.query(sql, error => {
218
+ m.query(sql, (error) => {
226
219
  if (error) {
227
220
  reject(new Error(`failed: ${error}`));
228
221
  return;
229
222
  }
230
223
 
231
- logger.log('SQL deplopyment was successful');
224
+ logger.log('SQL deployment was successful');
232
225
  resolve();
233
226
  });
234
227
  });
@@ -236,7 +229,7 @@ async function deployMySQL(file) {
236
229
 
237
230
  async function disconnectMySQL() {
238
231
  return new Promise((resolve, reject) => {
239
- m.end(err => {
232
+ m.end((err) => {
240
233
  if (err) {
241
234
  reject(new Error(`Error disconnecting: ${err.stack}`));
242
235
  return;
@@ -255,10 +248,9 @@ module.exports = async (selectOptions, callbacks) => {
255
248
  };
256
249
 
257
250
  settings = getSettings();
258
- setDeployModeOptions(settings);
259
251
 
260
252
  if (options.mode !== DEPLOY_MODES.SQL_ONLY) {
261
- if (!isCI() && options.environment === 'production') {
253
+ if (options.environment === 'production') {
262
254
  try {
263
255
  const unstagedFiles = await git.getUnstagedFiles();
264
256
  if (unstagedFiles.length > 0) {
@@ -271,17 +263,16 @@ module.exports = async (selectOptions, callbacks) => {
271
263
  }
272
264
  }
273
265
 
274
- // Run the build task in a separate process
275
- await runProjectBuildTask(options.environment);
266
+ // Run the build task in a separate process, outputs to dist-deploy
267
+ const deployBuildDir = await runProjectBuildTask(options.environment);
268
+ setDeployModeOptions(deployBuildDir);
276
269
  await deployToServer();
270
+
271
+ // Clean up the temporary deploy build directory
272
+ await rimraf(getProjectPath(deployBuildDir));
277
273
  }
278
274
 
279
275
  if (options.sql) {
280
- if (isCI()) {
281
- console.log(options);
282
- return deploySQLFromCI(options);
283
- }
284
-
285
276
  const file = await exportSql({ environment: options.environment });
286
277
  await createTunnel();
287
278
  await connectMySQL();
package/lib/importSQL.js CHANGED
@@ -10,7 +10,7 @@ const { SQL_BACKUPS_DIR } = require('../constants');
10
10
  const { sqlPath } = require('../utils/getSqlPath');
11
11
 
12
12
  async function importSQL(database, file) {
13
- const importCommand = `${sqlPath}/mysql -uroot -proot ${database} < "${file}"`;
13
+ const importCommand = `${sqlPath}/mysql --force -uroot -proot ${database} < "${file}"`;
14
14
 
15
15
  await execAsync(importCommand);
16
16
  logger.log('Imported SQL successfully');
package/lib/index.js CHANGED
@@ -5,7 +5,6 @@ const devProject = require('./devProject');
5
5
  const exportSQL = require('./exportSQL');
6
6
  const importSQL = require('./importSQL');
7
7
  const setupProject = require('./setupProject');
8
- const addSettings = require('./addSettings');
9
8
 
10
9
  module.exports = {
11
10
  backupProject,
@@ -15,5 +14,4 @@ module.exports = {
15
14
  exportSQL,
16
15
  importSQL,
17
16
  setupProject,
18
- addSettings,
19
17
  };
package/lib/pullSQL.js CHANGED
@@ -28,9 +28,7 @@ module.exports = async (options) => {
28
28
  fs.mkdirSync(destinationDir, { recursive: true });
29
29
  }
30
30
 
31
- // 14/10/24 Fixes issue where /*!999999\- enable the sandbox mode */ is added as first line of SQL dump
32
- const sed = `| sed '/\\/\\*!999999/d'`;
33
- const command = `mysqldump --add-drop-table --user='${user}' --password='${password}' ${database} ${sed}`;
31
+ const command = `mysqldump --add-drop-table --user='${user}' --password='${password}' ${database}`;
34
32
 
35
33
  let privateKey;
36
34
  if (options.serverPrivateKey) {
@@ -6,7 +6,7 @@ const replace = require('replace-in-file');
6
6
  const logger = require('../logger');
7
7
  const getProjectPath = require('../utils/getProjectPath');
8
8
 
9
- const getReplacements = options => [
9
+ const getReplacements = (options) => [
10
10
  ['{{projectLocation}}', options.projectLocation],
11
11
  ['{{projectSlug}}', slugify(options.projectSlug).toLowerCase()],
12
12
  ['{{projectName}}', options.project],
@@ -16,11 +16,10 @@ const getReplacements = options => [
16
16
  ['{{tablePrefix}}', options.tablePrefix],
17
17
  ['{{database}}', options.database],
18
18
  ['{{productionDomain}}', options.productionDomain],
19
- ['{{productionDomain}}', options.productionDomain],
20
19
  ['project-slug-package-json', slugify(options.projectSlug).toLowerCase()],
21
20
  ];
22
21
 
23
- module.exports = async options => {
22
+ module.exports = async (options) => {
24
23
  const projectPath = getProjectPath();
25
24
  const filesToReplace = [
26
25
  ...glob.sync('config/*', { cwd: projectPath, dot: true }),
@@ -38,15 +37,14 @@ module.exports = async options => {
38
37
  let files = filesToReplace;
39
38
  if (getProjectPath() !== process.cwd()) {
40
39
  // Check if the script is being run from outside of the project and then fix the paths
41
- files = files.map(file => getProjectPath(file));
40
+ files = files.map((file) => getProjectPath(file));
42
41
  }
43
42
 
44
43
  // Remove any files that don't exist
45
44
  files = files.filter(fs.existsSync);
46
45
 
47
46
  // Run sequentially to ensure replace only operates on one file at a time
48
- for (var i = 0; i < replacements.length; i++) {
49
- const [from, to] = replacements[i];
47
+ for (const [from, to] of replacements) {
50
48
  await replace({
51
49
  files,
52
50
  from: new RegExp(from, 'g'),
@@ -1,4 +1,5 @@
1
1
  const fs = require('fs');
2
+ const path = require('path');
2
3
  const chalk = require('chalk');
3
4
 
4
5
  const logger = require('../logger');
@@ -23,31 +24,39 @@ async function checkProjectDependencies() {
23
24
  logger.warn('You are using an old version of fx-classes, please upgrade to at least v2.');
24
25
  }
25
26
 
26
- // Husky pre-commit hooks activation and migration from v4 to v8
27
27
  const huskyDirectory = getProjectPath('.husky');
28
28
  const oldHuskyConfig = getProjectPath('.huskyrc.js');
29
+ const preCommitHook = getProjectPath('.husky', 'pre-commit');
30
+ const postMergeHook = getProjectPath('.husky', 'post-merge');
29
31
 
30
- // Delete old config if it exists
32
+ // Migrate away from legacy .huskyrc.js (v4)
31
33
  if (fs.existsSync(oldHuskyConfig)) {
32
34
  try {
33
35
  fs.rmSync(oldHuskyConfig);
34
36
  logger.info('Old Husky config deleted');
35
37
  } catch (err) {
36
- console.warn('Error while deleting old Husky config:', err);
38
+ logger.warn('Error while deleting old Husky config:', err);
37
39
  }
38
40
  }
39
41
 
40
- if (!fs.existsSync(huskyDirectory)) {
41
- // Husky now requires a "one-time" explicit git hook activation
42
- const husky = `node_modules/.bin/husky`;
43
-
44
- await execAsync(`${husky} install`);
45
- logger.info('Upgrading Husky git hooks config');
46
- await execAsync(`${husky} add .husky/pre-commit 'npx lint-staged'`);
47
- await execAsync(
48
- `${husky} add .husky/post-merge 'node_modules/.bin/wp-scripts hooks --type="post-merge"'`
42
+ // Detect v8-format hooks (contain the _/husky.sh source line)
43
+ const hasV8Hook =
44
+ fs.existsSync(preCommitHook) && fs.readFileSync(preCommitHook, 'utf8').includes('_/husky.sh');
45
+
46
+ const husky = path.resolve(__dirname, '..', 'node_modules', '.bin', 'husky');
47
+
48
+ if (!fs.existsSync(huskyDirectory) || hasV8Hook) {
49
+ // Resolve husky from wp-blank-scripts' own node_modules so it works regardless
50
+ // of how the project manages its dependencies (yarn, pnpm, npm link, etc.)
51
+ await execAsync(`${husky}`);
52
+ fs.mkdirSync(huskyDirectory, { recursive: true });
53
+ fs.writeFileSync(preCommitHook, '#!/bin/sh\nnode_modules/.bin/lint-staged\n', { mode: 0o755 });
54
+ fs.writeFileSync(
55
+ postMergeHook,
56
+ '#!/bin/sh\nnode_modules/.bin/wp-scripts hooks --type="post-merge"\n',
57
+ { mode: 0o755 }
49
58
  );
50
- logger.success('Husky upgrade successful!');
59
+ logger.success(hasV8Hook ? 'Husky git hooks migrated to v9' : 'Husky git hooks installed');
51
60
  }
52
61
  }
53
62
 
@@ -8,10 +8,8 @@ if (fs.existsSync(overridesPath)) {
8
8
  }
9
9
 
10
10
  const checkProjectDependencies = require('./checkProjectDependencies');
11
- const copyFx = require('./copyFx');
12
11
 
13
12
  module.exports = {
14
13
  checkProjectDependencies,
15
- copyFx,
16
14
  ...overrides,
17
15
  };