wp-advads 1.0.31 → 1.0.34

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.
Files changed (89) hide show
  1. package/.github/workflows/test.yml +17 -0
  2. package/CHANGELOG.md +23 -1
  3. package/LICENSE +15 -0
  4. package/README.md +8 -7
  5. package/package.json +9 -7
  6. package/src/app.js +17 -11
  7. package/src/commands/build.js +72 -59
  8. package/src/commands/class.js +34 -40
  9. package/src/commands/css.js +13 -19
  10. package/src/commands/init.js +4 -1
  11. package/src/commands/javascript.js +13 -19
  12. package/src/commands/release/change-version.js +14 -17
  13. package/src/commands/release/changelog.js +23 -31
  14. package/src/commands/release/git.js +25 -13
  15. package/src/commands/release/github.js +63 -12
  16. package/src/commands/release/semver.js +22 -25
  17. package/src/commands/release/translations.js +17 -23
  18. package/src/commands/release.js +3 -4
  19. package/src/commands/updates.js +20 -17
  20. package/src/commands/views.js +16 -18
  21. package/src/commands/wp-glotpress.js +125 -126
  22. package/src/commands/wp-pot.js +17 -9
  23. package/src/utilities/filesystem.js +57 -36
  24. package/src/utilities/flow.js +21 -0
  25. package/src/utilities/formatting.js +13 -4
  26. package/src/utilities/index.js +1 -1
  27. package/src/utilities/misc.js +1 -6
  28. package/src/utilities/settings.js +34 -14
  29. package/src/utilities/shell.js +60 -8
  30. package/template/files/class-singleton.php +5 -0
  31. package/template/files/javascript.js +0 -2
  32. package/template/files/view.php +2 -0
  33. package/test/build.test.js +168 -0
  34. package/test/changelog-locales.test.js +49 -0
  35. package/test/class.test.js +47 -0
  36. package/test/compile-templates.test.js +70 -0
  37. package/test/deps-hygiene.test.js +46 -0
  38. package/test/engines.test.js +11 -0
  39. package/test/get-latest-tag.test.js +51 -0
  40. package/test/git-precheck.test.js +44 -0
  41. package/test/github-final.test.js +93 -0
  42. package/test/glotpress-download.test.js +176 -0
  43. package/test/nested-path.test.js +39 -0
  44. package/test/no-fs-extra.test.js +11 -0
  45. package/test/package-identity.test.js +19 -0
  46. package/test/path-confine.test.js +42 -0
  47. package/test/pot-docs.test.js +27 -0
  48. package/test/pot-headers.test.js +49 -0
  49. package/test/project-root.test.js +26 -0
  50. package/test/release-waterfall.test.js +62 -0
  51. package/test/semver.test.js +25 -0
  52. package/test/settings-clone.test.js +34 -0
  53. package/test/settings-path.test.js +31 -0
  54. package/test/shell.test.js +100 -0
  55. package/test/singleton.test.js +14 -0
  56. package/test/stable-tag.test.js +13 -0
  57. package/test/stub-security.test.js +37 -0
  58. package/test/template-folder.test.js +19 -0
  59. package/test/translations.test.js +51 -0
  60. package/test/update-file-content.test.js +38 -0
  61. package/test/updates.test.js +39 -0
  62. package/test/version.test.js +22 -0
  63. package/test/view-escape.test.js +17 -0
  64. package/test/view.test.js +33 -0
  65. package/test/write-force.test.js +47 -0
  66. package/src/utilities/semver.js +0 -45
  67. package/template/.github/pull_request_template.md +0 -52
  68. package/template/configs/.editorconfig +0 -27
  69. package/template/configs/.eslintignore +0 -13
  70. package/template/configs/.eslintrc.js +0 -13
  71. package/template/configs/.phpcs.xml.dist +0 -87
  72. package/template/configs/.prettierignore +0 -49
  73. package/template/configs/.prettierrc.js +0 -3
  74. package/template/configs/.stylelintrc +0 -44
  75. package/template/configs/composer.json +0 -51
  76. package/template/configs/gitattributes +0 -34
  77. package/template/configs/gitignore +0 -43
  78. package/template/configs/package.json +0 -66
  79. package/template/configs/postcss.config.js +0 -6
  80. package/template/configs/tailwind.config.js +0 -8
  81. package/template/configs/webpack.mix.js +0 -65
  82. package/template/configs/webpack.mix.local.js +0 -3
  83. package/template/includes/index.php +0 -2
  84. package/template/index.js +0 -0
  85. package/template/index.php +0 -4
  86. package/template/plugin/assets/scss/app.scss +0 -0
  87. package/template/plugin/assets/src/app.js +0 -0
  88. package/template/tools/laravel-mix/wp-pot.js +0 -57
  89. package/template/tools/wp-glotpress.js +0 -162
@@ -4,163 +4,162 @@
4
4
  import fs from 'fs';
5
5
  import chalk from 'chalk';
6
6
  import { resolve } from 'path';
7
- import eachSeries from 'async/eachSeries.js';
8
-
9
- /**
10
- * Internal Dependencies
11
- */
12
7
  import { heading, msgError, getSetting, write, msgSuccessOnSameLine, onSameLine, msgErrorOnSameLine } from "../utilities/index.js";
13
8
 
14
- class GlotPressDownloader {
15
- baseURL = 'https://translate.wpadvancedads.com/api/projects';
9
+ const BASE_URL = 'https://translate.wpadvancedads.com/api/projects';
16
10
 
17
- run() {
18
- heading('Downloading Translations from GlotPress...')
11
+ export async function downloadTranslations() {
12
+ heading('Downloading Translations from GlotPress...')
19
13
 
20
- try {
21
- this.validateData();
22
- this.maybeDirectory();
23
- this.download();
24
- } catch (error) {
25
- msgError(error.message);
26
- return;
27
- }
14
+ try {
15
+ const ctx = readProject();
16
+ ensureDestination( ctx.destination );
17
+ return await downloadAll( ctx );
18
+ } catch (error) {
19
+ msgError(error.message);
20
+ throw error;
28
21
  }
22
+ }
29
23
 
30
- validateData() {
31
- const glotpress = getSetting('glotpress')
32
- write('Validating data...');
24
+ export default () => {
25
+ downloadTranslations().catch(() => {});
26
+ }
33
27
 
34
- if (!glotpress) {
35
- onSameLine('')
36
- throw new Error('No glotpress project found');
37
- }
28
+ function readProject() {
29
+ const glotpress = getSetting('glotpress')
30
+ write('Validating data...');
38
31
 
39
- if ('' === glotpress.project) {
40
- throw new Error('The GlotPress project name is not defined.');
41
- }
32
+ if (!glotpress) {
33
+ onSameLine('')
34
+ throw new Error('No glotpress project found');
35
+ }
36
+
37
+ if ('' === glotpress.project) {
38
+ throw new Error('The GlotPress project name is not defined.');
39
+ }
40
+
41
+ if (undefined === glotpress.destination || '' === glotpress.destination) {
42
+ throw new Error('The destination directory is not defined.');
43
+ }
44
+
45
+ const { project, destination } = glotpress;
46
+ const domain = getSetting('wpPot.domain');
42
47
 
43
- if (undefined === glotpress.destination || '' === glotpress.destination) {
44
- throw new Error('The destination directory is not defined.');
45
- }
48
+ msgSuccessOnSameLine('Validation done');
49
+
50
+ return {
51
+ project,
52
+ potPrefix: domain ?? project,
53
+ destination,
54
+ }
55
+ }
46
56
 
47
- // Set data.
48
- const { project, destination } = glotpress;
49
- const domain = getSetting('wpPot.domain');
57
+ function ensureDestination( destination ) {
58
+ write('Checking if destination directory exists...');
59
+ fs.mkdirSync(resolve(destination), { recursive: true });
60
+ msgSuccessOnSameLine('Directory exists');
61
+ }
50
62
 
51
- this.project = project;
52
- this.potPrefix = domain ?? project;
53
- this.destination = destination;
63
+ async function downloadAll( ctx ) {
64
+ const locales = await getLocales( ctx );
65
+ if (false === locales) {
66
+ return { files: [], locales: {} };
67
+ }
54
68
 
55
- msgSuccessOnSameLine('Validation done');
56
- }
69
+ const files = [];
70
+ const names = {};
57
71
 
58
- maybeDirectory() {
59
- const destination = resolve(this.destination);
60
- write('Checking if destination directory exists...');
72
+ for ( const locale of Object.keys( locales ) ) {
73
+ const set = locales[ locale ];
61
74
 
62
- if (!fs.existsSync(destination)) {
63
- fs.mkdirSync(destination);
75
+ console.log( '' );
76
+ console.log(
77
+ chalk.yellow.bold( 'Updating Language:' ) +
78
+ ` ${chalk.italic( set.name )}`
79
+ );
64
80
 
65
- if (!fs.existsSync(destination)) {
66
- throw new Error('The destination directory could not be created.');
67
- } else {
68
- msgSuccessOnSameLine('Directory created');
81
+ for ( const format of [ 'mo', 'po' ] ) {
82
+ const target = await downloadFile( ctx, locale, set, format );
83
+ if ( target ) {
84
+ const file = target.replace( /^\.\//, '' );
85
+ files.push( file );
86
+ names[ file ] = set.name;
69
87
  }
70
- } else {
71
- msgSuccessOnSameLine('Directory exists');
72
88
  }
73
- }
74
-
75
- async download() {
76
- const locales = await this.getLocales();
77
- if (false === locales) {
78
- return;
79
- }
80
-
81
- eachSeries(
82
- Object.keys(locales),
83
- (locale, callback) => {
84
- const set = locales[locale];
85
-
86
- console.log('');
87
- console.log(
88
- chalk.yellow.bold('Updating Language:') +
89
- ` ${chalk.italic(set.name)}`
90
- );
91
-
92
- eachSeries(['mo', 'po'], (format, nextFormat) => {
93
- this.downloadFile(locale, set, format, nextFormat);
94
- }, (err) => {
95
- callback();
96
- })
97
- },
98
- (err, results) => {
99
- if (err) {
100
- msgError(err.message);
101
- } else {
102
- console.log('');
103
- console.log(
104
- chalk.bgGreen.bold('Success:') +
105
- ' All files has been downloaded.'
106
- );
107
- }
108
- }
109
- )
110
89
  }
111
90
 
112
- async getLocales() {
113
- write('Downloading locales...');
114
- const response = await fetch(`${this.baseURL}/${this.project}`);
115
- const data = await response.json();
116
- const sets = {};
91
+ console.log( '' );
92
+ console.log(
93
+ chalk.bgGreen.bold( 'Success:' ) +
94
+ ' All files has been downloaded.'
95
+ );
96
+
97
+ return { files, locales: names };
98
+ }
99
+
100
+ async function getLocales( ctx ) {
101
+ write('Downloading locales...');
102
+ const response = await fetch(`${BASE_URL}/${ctx.project}`, {
103
+ signal: AbortSignal.timeout(30000),
104
+ });
105
+ if (!response.ok) {
106
+ msgErrorOnSameLine(`HTTP ${response.status}`);
107
+ return false;
108
+ }
109
+ const data = await response.json();
110
+ const sets = {};
111
+
112
+ if (undefined !== data.success && !data.success) {
113
+ msgErrorOnSameLine(data.error);
114
+ return false;
115
+ }
117
116
 
118
- if (undefined !== data.success && !data.success) {
119
- msgErrorOnSameLine(data.error);
120
- return false;
121
- }
117
+ data.translation_sets.map((set) => {
118
+ if (set.current_count > 0) {
119
+ let id = set.wp_locale;
120
+ if ('default' !== set.slug) {
121
+ id += '_' + set.slug;
122
+ }
122
123
 
123
- data.translation_sets.map((set) => {
124
- if (set.current_count > 0) {
125
- let id = set.wp_locale;
126
- if ('default' !== set.slug) {
127
- id += '_' + set.slug;
128
- }
124
+ sets[id] = set;
125
+ }
129
126
 
130
- sets[id] = set;
131
- }
127
+ return false;
128
+ });
132
129
 
133
- return false;
134
- });
130
+ msgSuccessOnSameLine('Downloaded locales');
135
131
 
136
- msgSuccessOnSameLine('Downloaded locales');
132
+ return sets;
133
+ }
137
134
 
138
- return sets;
135
+ async function downloadFile(ctx, locale, data, format) {
136
+ if (!/^[a-zA-Z0-9_]+$/.test(locale)) {
137
+ msgErrorOnSameLine(`Skipping invalid locale: ${locale}`);
138
+ return;
139
139
  }
140
140
 
141
- async downloadFile(locale, data, format, callback) {
142
- write(
143
- chalk.bold(`Downloading: ${this.potPrefix}-${locale}.${format}`)
144
- );
141
+ write(
142
+ chalk.bold(`Downloading: ${ctx.potPrefix}-${locale}.${format}`)
143
+ );
145
144
 
146
- const target = `${this.destination}/${this.potPrefix}-${locale}.${format}`;
147
- const endpoint = `${this.baseURL}/${this.project}/${data.locale}/${data.slug}/export-translations?format=${format}`;
145
+ const target = `${ctx.destination}/${ctx.potPrefix}-${locale}.${format}`;
146
+ const endpoint = `${BASE_URL}/${ctx.project}/${data.locale}/${data.slug}/export-translations?format=${format}`;
148
147
 
149
- // Download.
150
- const response = await fetch(endpoint);
151
- const content = await response.text();
148
+ const response = await fetch(endpoint, {
149
+ signal: AbortSignal.timeout(30000),
150
+ });
151
+ if (!response.ok) {
152
+ msgErrorOnSameLine(`Skipping ${ctx.potPrefix}-${locale}.${format}`);
153
+ return;
154
+ }
152
155
 
153
- fs.writeFileSync(target, content);
156
+ const content = await response.text();
154
157
 
155
- onSameLine(
156
- chalk.bold(`Downloaded: ${this.potPrefix}-${locale}.${format}`)
157
- );
158
+ fs.writeFileSync(target, content);
158
159
 
159
- callback();
160
- }
161
- }
160
+ onSameLine(
161
+ chalk.bold(`Downloaded: ${ctx.potPrefix}-${locale}.${format}`)
162
+ );
162
163
 
163
- export default () => {
164
- const downloader = new GlotPressDownloader();
165
- downloader.run();
164
+ return target;
166
165
  }
@@ -4,16 +4,18 @@
4
4
  import { heading, msgSuccess, runCommand, getSetting } from "../utilities/index.js"
5
5
 
6
6
  export default (next = null) => {
7
- heading('Generating PO and MO files...')
7
+ heading('Generating POT file...')
8
8
 
9
9
  const settings = getSetting()
10
10
 
11
11
  const output = settings.wpPot.output;
12
12
  const file = settings.wpPot.file;
13
13
  const domain = settings.wpPot.domain;
14
+ const name = settings.author?.name ?? ''
15
+ const email = settings.author?.email ?? ''
14
16
  const headers = {
15
- 'Last-Translator': 'Shakeeb Ahmed <me@shakeebahmed.com>',
16
- 'Language-Team': 'Advanced Ads <support@wpadvancedads.com>',
17
+ 'Last-Translator': `${name} <${email}>`,
18
+ 'Language-Team': `${name} <${email}>`,
17
19
  };
18
20
  const exclude = [
19
21
  '.git',
@@ -28,17 +30,23 @@ export default (next = null) => {
28
30
 
29
31
  // Execute.
30
32
  const rootPath = process.cwd();
31
- const command = [
32
- 'wp i18n make-pot',
33
+ runCommand( 'wp', [
34
+ 'i18n',
35
+ 'make-pot',
33
36
  rootPath,
34
37
  rootPath + output + file,
35
38
  `--domain=${domain}`,
36
39
  `--exclude=${exclude.join(',')}`,
37
- `--headers='${JSON.stringify(headers)}'`,
38
- ];
40
+ `--headers=${JSON.stringify(headers)}`,
41
+ ], ( error ) => {
42
+ if ( error ) {
43
+ if ( next ) {
44
+ next( error )
45
+ }
46
+ return
47
+ }
39
48
 
40
- runCommand( command.join(' '), [], () => {
41
- msgSuccess('PO and MO files generated successfully');
49
+ msgSuccess('POT file generated successfully');
42
50
  if (next) next();
43
51
  } );
44
52
  }
@@ -1,7 +1,6 @@
1
1
  /**
2
2
  * External dependencies
3
3
  */
4
- import chalk from 'chalk';
5
4
  import logSymbols from 'log-symbols';
6
5
  import template from 'lodash/template.js';
7
6
 
@@ -9,12 +8,13 @@ import template from 'lodash/template.js';
9
8
  * Node dependencies
10
9
  */
11
10
  import path from 'path';
12
- import fs from 'fs-extra';
11
+ import fs from 'node:fs';
12
+ import { fileURLToPath } from 'node:url';
13
13
 
14
14
  /**
15
15
  * Internal Dependencies
16
16
  */
17
- import { msgSuccessOnSameLine, onSameLine, write } from './formatting.js'
17
+ import { msgErrorTitle, msgSuccessOnSameLine, onSameLine, write } from './formatting.js'
18
18
 
19
19
  let projectRoot = null
20
20
 
@@ -32,32 +32,26 @@ export function getProjectRoot(startDir = process.cwd()) {
32
32
  return projectRoot;
33
33
  }
34
34
 
35
- projectRoot = startDir;
35
+ let dir = startDir;
36
36
 
37
- while (projectRoot !== path.parse(projectRoot).root) {
38
- const possibleRootFile = path.join(projectRoot, 'wp.advads');
39
- if (fs.existsSync(possibleRootFile)) {
37
+ while (dir !== path.parse(dir).root) {
38
+ if (fs.existsSync(path.join(dir, 'wp.advads'))) {
39
+ projectRoot = dir;
40
40
  return projectRoot;
41
41
  }
42
42
 
43
- // Move up one directory level
44
- projectRoot = path.dirname(projectRoot);
45
- }
43
+ dir = path.dirname(dir);
44
+ }
46
45
 
47
- throw new Error('Project root not found. Make sure there is a wp.advads file in the root directory.');
46
+ projectRoot = null;
47
+ throw new Error('Project root not found. Make sure there is a wp.advads file in the root directory.');
48
48
  }
49
49
 
50
50
  /**
51
- * Retrieves the path to the template folder.
52
- *
53
- * This function uses `createRequire` to resolve the path to the `../../template` directory
54
- * relative to the current module's URL and removes the `/index.js` part from the resolved path.
55
- *
56
51
  * @returns {string} The path to the template folder.
57
52
  */
58
53
  export function getTemplateFolder() {
59
- const dirname = path.dirname(import.meta.url.replace('file://', ''))
60
- return path.join(dirname, '../../template')
54
+ return path.join( path.dirname( fileURLToPath( import.meta.url ) ), '../../template' )
61
55
  }
62
56
 
63
57
  /**
@@ -87,21 +81,22 @@ export function compileTemplate( filename, data ) {
87
81
  }
88
82
 
89
83
  /**
90
- * Deletes a specified file if it exists.
84
+ * Compile a template and write it, or print the shared scaffold failure.
91
85
  *
92
- * @param {string} name - The name of the file to be deleted.
93
- * @param {string} file - The path to the file to be deleted.
94
- *
95
- * @returns {void}
86
+ * @param {string} folder
87
+ * @param {string} filename
88
+ * @param {string} templateFile Path under template/
89
+ * @param {Object} data
90
+ * @param {boolean} force
96
91
  */
97
- export function deleteFile(name, file) {
98
- if ( ! fs.existsSync(file) ) {
99
- console.log( `${logSymbols.error} ${chalk.red(`${name} file not found!`)}` )
100
- return
101
- }
102
-
103
- fs.rmSync(file)
104
- console.log( `${logSymbols.success} ${chalk.dim(`${name} deleted!`)}` )
92
+ export function writeFromTemplate( folder, filename, templateFile, data, force = false ) {
93
+ try {
94
+ const content = compileTemplate( getTemplateFile( templateFile ), data )
95
+ writeFile( folder, filename, content, force )
96
+ } catch ( err ) {
97
+ msgErrorTitle( 'We failed!!!' )
98
+ throw err
99
+ }
105
100
  }
106
101
 
107
102
  /**
@@ -120,7 +115,7 @@ export function updateFileContent(fileName, messages, next, callback) {
120
115
  fs.readFile(fileName, (err, buffer) => {
121
116
  if ( null !== err ) {
122
117
  onSameLine(`${logSymbols.error} ${messages.failed}`)
123
- return next()
118
+ return next(err)
124
119
  }
125
120
 
126
121
  const content = callback(buffer.toString())
@@ -128,7 +123,7 @@ export function updateFileContent(fileName, messages, next, callback) {
128
123
  fs.writeFile(fileName, content, (err) => {
129
124
  if ( null !== err ) {
130
125
  onSameLine(`${logSymbols.error} ${messages.failed}`)
131
- return next()
126
+ return next(err)
132
127
  }
133
128
 
134
129
  onSameLine(`${logSymbols.success} ${messages.updated}`)
@@ -137,12 +132,38 @@ export function updateFileContent(fileName, messages, next, callback) {
137
132
  })
138
133
  }
139
134
 
140
- export function writeFile(folder, filename, content) {
135
+ function assertInsideProject( target ) {
136
+ if ( String( target ).includes( '\0' ) ) {
137
+ throw new Error( 'Invalid path' )
138
+ }
139
+
140
+ const root = path.resolve( getProjectRoot() )
141
+ const resolved = path.resolve( target )
142
+ const relative = path.relative( root, resolved )
143
+
144
+ if ( relative.startsWith( `..${path.sep}` ) || relative === '..' || path.isAbsolute( relative ) ) {
145
+ throw new Error( 'Path escapes project root' )
146
+ }
147
+ }
148
+
149
+ export function writeFile(folder, filename, content, force = false) {
150
+ if ( String( filename ).includes( '\0' ) ) {
151
+ throw new Error( 'Invalid path' )
152
+ }
153
+
154
+ assertInsideProject( folder )
155
+ assertInsideProject( path.join( folder, filename ) )
156
+
157
+ const target = path.join( folder, filename )
158
+ if ( ! force && fs.existsSync( target ) ) {
159
+ throw new Error( `File exists: ${target}` )
160
+ }
161
+
141
162
  write('Creating directories!!');
142
- fs.ensureDirSync(folder);
163
+ fs.mkdirSync(folder, { recursive: true });
143
164
  msgSuccessOnSameLine('Directories created successfully');
144
165
 
145
166
  write('Creating file!!');
146
- fs.writeFileSync(path.join(folder, filename), content);
167
+ fs.writeFileSync(target, content);
147
168
  msgSuccessOnSameLine('File created successfully');
148
169
  }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Run callback-style steps in order. `next(err)` if any step fails.
3
+ *
4
+ * @param {Function[]} steps Each step is `(done) => void`
5
+ * @param {Function} next
6
+ */
7
+ export function runSteps( steps, next ) {
8
+ ;( async () => {
9
+ for ( const step of steps ) {
10
+ await new Promise( ( resolve, reject ) => {
11
+ step( ( err ) => {
12
+ if ( err ) {
13
+ reject( err )
14
+ } else {
15
+ resolve()
16
+ }
17
+ } )
18
+ } )
19
+ }
20
+ } )().then( () => next() ).catch( ( err ) => next( err ) )
21
+ }
@@ -28,6 +28,10 @@ export function onNewLine(text) {
28
28
  console.log(text)
29
29
  }
30
30
 
31
+ function canRewriteStdout() {
32
+ return Boolean( process.stdout.isTTY && ! process.env.NODE_TEST_CONTEXT )
33
+ }
34
+
31
35
  /**
32
36
  * Print text on same line by clearing buffer.
33
37
  *
@@ -36,9 +40,9 @@ export function onNewLine(text) {
36
40
  * @returns {void}
37
41
  */
38
42
  export function onSameLine(text) {
39
- if (process.stdout.clearLine) {
40
- process.stdout.clearLine(); // Clear the current line
41
- process.stdout.cursorTo(0); // Move the cursor to the start of the line
43
+ if ( canRewriteStdout() && process.stdout.clearLine ) {
44
+ process.stdout.clearLine();
45
+ process.stdout.cursorTo(0);
42
46
  }
43
47
 
44
48
  console.log(text)
@@ -118,5 +122,10 @@ export function msgErrorTitle(msg) {
118
122
  * @returns {void}
119
123
  */
120
124
  export function write(text) {
121
- process.stdout.write(text)
125
+ if ( canRewriteStdout() ) {
126
+ process.stdout.write(text)
127
+ return
128
+ }
129
+
130
+ console.log(text)
122
131
  }
@@ -1,6 +1,6 @@
1
1
  export * from './filesystem.js'
2
+ export * from './flow.js'
2
3
  export * from './formatting.js'
3
- export * from './semver.js'
4
4
  export * from './settings.js'
5
5
  export * from './shell.js'
6
6
  export * from './misc.js'
@@ -1,12 +1,7 @@
1
- /**
2
- * External Dependencies
3
- */
4
- import capitalize from 'lodash/capitalize.js';
5
-
6
1
  export function filenameToHeading(filename) {
7
2
  filename = filename.replace('.php', '');
8
3
  filename = filename.replace('.js', '');
9
4
  filename = filename.replace(/-/g, ' ');
10
5
 
11
- return capitalize(filename);
6
+ return filename.charAt(0).toUpperCase() + filename.slice(1).toLowerCase();
12
7
  }
@@ -1,9 +1,3 @@
1
- /**
2
- * External dependencies
3
- */
4
- import get from 'lodash/get.js'
5
- import set from 'lodash/set.js'
6
-
7
1
  /**
8
2
  * Node dependencies
9
3
  */
@@ -18,6 +12,14 @@ import { write } from './formatting.js'
18
12
 
19
13
  let settings = null
20
14
 
15
+ function configPath() {
16
+ try {
17
+ return path.join( getProjectRoot(), 'wp.advads' )
18
+ } catch {
19
+ return path.join( process.cwd(), 'wp.advads' )
20
+ }
21
+ }
22
+
21
23
  /**
22
24
  * Checks if the configuration file exists at the specified path.
23
25
  *
@@ -25,8 +27,7 @@ let settings = null
25
27
  */
26
28
  export function configFileExists() {
27
29
  try {
28
- const configPath = path.join(getProjectRoot(), 'wp.advads')
29
- return fs.existsSync(configPath);
30
+ return fs.existsSync( configPath() );
30
31
  } catch (err) {
31
32
  return false;
32
33
  }
@@ -39,8 +40,7 @@ export function configFileExists() {
39
40
  */
40
41
  export function readConfigFile() {
41
42
  try {
42
- const configPath = path.join(getProjectRoot(), 'wp.advads')
43
- const data = fs.readFileSync(configPath, 'utf-8');
43
+ const data = fs.readFileSync( configPath(), 'utf-8' );
44
44
  return JSON.parse(data);
45
45
  } catch (err) {
46
46
  return {};
@@ -55,9 +55,8 @@ export function readConfigFile() {
55
55
  export function saveConfig(data) {
56
56
  write('Saving config file...');
57
57
  try {
58
- const savePath = path.join(process.cwd(), 'wp.advads')
59
58
  const jsonData = JSON.stringify(data, null, 2);
60
- fs.writeFileSync(savePath, jsonData);
59
+ fs.writeFileSync( configPath(), jsonData );
61
60
  settings = data;
62
61
  } catch (err) {
63
62
  write('Error writing config file:', err);
@@ -82,7 +81,7 @@ export function getSetting(key = 'all', defaultVal = '') {
82
81
  return settings;
83
82
  }
84
83
 
85
- return get( settings, key, defaultVal );
84
+ return getPath( settings, key, defaultVal );
86
85
  }
87
86
 
88
87
  export function updateSetting(key, value) {
@@ -90,6 +89,27 @@ export function updateSetting(key, value) {
90
89
  settings = readConfigFile();
91
90
  }
92
91
 
93
- settings = set(settings, key, value);
92
+ settings = setPath(settings, key, value);
94
93
  saveConfig(settings);
95
94
  }
95
+
96
+ function getPath( object, key, defaultVal ) {
97
+ const value = String( key ).split( '.' ).reduce( ( cur, part ) => cur?.[ part ], object )
98
+ return value === undefined ? defaultVal : value
99
+ }
100
+
101
+ function setPath( object, key, value ) {
102
+ const parts = String( key ).split( '.' )
103
+ let cur = object
104
+
105
+ for ( let i = 0; i < parts.length - 1; i++ ) {
106
+ const part = parts[ i ]
107
+ if ( cur[ part ] == null || typeof cur[ part ] !== 'object' ) {
108
+ cur[ part ] = {}
109
+ }
110
+ cur = cur[ part ]
111
+ }
112
+
113
+ cur[ parts[ parts.length - 1 ] ] = value
114
+ return object
115
+ }