wp-advads 1.0.33 → 1.0.35

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 +86 -66
  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 -11
  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
@@ -7,38 +7,32 @@ import logSymbols from 'log-symbols'
7
7
  * Internal Dependencies
8
8
  */
9
9
  import { pluginData } from '../release.js'
10
- import { heading, execCommand, getSetting } from "../../utilities/index.js"
10
+ import { heading } from "../../utilities/index.js"
11
+ import { downloadTranslations } from '../wp-glotpress.js'
11
12
 
12
13
  /**
13
14
  * Execute routine to download translations from GlotPress
14
15
  */
15
- export default function updateTranslations( next ) {
16
+ export default async function updateTranslations( next ) {
16
17
  heading('Downloading translations from glotpress')
17
18
 
18
- const glotpress = getSetting('wp.glotpress')
19
+ try {
20
+ const { files, locales } = await downloadTranslations()
21
+ console.log( logSymbols.success + ` Downloaded ${files.length} files successfully` )
19
22
 
20
- if (!glotpress) {
21
- console.log( logSymbols.warning + ' No glotpress project found' )
22
- next()
23
- return;
24
- }
25
-
26
- execCommand( 'npx wp-advads translations', function(locales) {
27
- locales = locales.split('\n').slice(4).slice(0,-1)
28
- console.log( logSymbols.success + ` Downloaded ${locales.length} files successfully` )
29
-
30
- if (locales.length > 0) {
23
+ if (files.length > 0) {
31
24
  pluginData.commitMessages.push('add: Download translation from GlotPress')
32
25
  }
33
26
 
34
- const newLocales = {}
35
- locales.map((locale) => {
36
- const data = locale.split('=')
37
- newLocales[ data[0] ] = data[1]
38
- })
39
-
40
- pluginData.locales = newLocales
41
-
27
+ pluginData.locales = locales
42
28
  next()
43
- })
29
+ } catch (err) {
30
+ if ( err && err.message === 'No glotpress project found' ) {
31
+ console.log( logSymbols.warning + ' No glotpress project found' )
32
+ next()
33
+ return
34
+ }
35
+
36
+ next(err)
37
+ }
44
38
  }
@@ -2,13 +2,12 @@
2
2
  * External Dependencies
3
3
  */
4
4
  import chalk from 'chalk'
5
- import { waterfall } from 'async'
6
5
  import logSymbols from 'log-symbols'
7
6
 
8
7
  /**
9
8
  * Node dependencies
10
9
  */
11
- import { heading } from '../utilities/index.js'
10
+ import { heading, runSteps } from '../utilities/index.js'
12
11
 
13
12
  /**
14
13
  * Internal Dependencies
@@ -31,13 +30,13 @@ export default async () => {
31
30
  heading('Releasing the plugin...')
32
31
  console.log('------------------------------------------------');
33
32
 
34
- waterfall(
33
+ runSteps(
35
34
  [
36
35
  prompts,
37
36
  getSemVer,
38
37
  githubPreTasks,
39
38
  updateTranslations,
40
- wpBuild,
39
+ ( next ) => wpBuild( next, pluginData.commitMessages ),
41
40
  updateVersionNumber,
42
41
  updateChangelog,
43
42
  githubFinalTasks
@@ -2,30 +2,33 @@
2
2
  * Node Dependencies
3
3
  */
4
4
  import path from 'path';
5
- import fs from 'fs-extra';
6
5
 
7
6
  /**
8
7
  * Internal Dependencies
9
8
  */
10
- import { getSetting, writeFile, heading, getProjectRoot, msgErrorTitle, getTemplateFile, compileTemplate } from "../utilities/index.js";
9
+ import { getSetting, heading, getProjectRoot, writeFromTemplate } from "../utilities/index.js";
11
10
 
12
- export default (version) => {
11
+ export default (version, options = {}) => {
13
12
  heading('Creating update file...')
14
13
 
15
- try {
16
- const filename = `upgrade-${version}.php`;
17
- const folder = path.join(getProjectRoot(), getSetting('paths.updates'));
18
-
19
- // Data
20
- const data = getSetting();
21
- data.heading = `Update routine for version ${version}`;
22
- data.version = version.replaceAll('.', '_');
23
-
24
- const content = compileTemplate(getTemplateFile('files/update.php'), data);
25
- writeFile(folder,filename, content)
14
+ const settings = getSetting()
15
+ const data = {
16
+ ...settings,
17
+ heading: `Update routine for version ${version}`,
18
+ description: `Update routine for version ${version}`,
19
+ prefix: settings.misc?.prefix,
20
+ version: version.replaceAll('.', '_'),
26
21
  }
27
- catch (err) {
28
- msgErrorTitle('We failed!!!');
29
- throw err;
22
+
23
+ if ( ! data.prefix ) {
24
+ throw new Error( 'misc.prefix is required' );
30
25
  }
26
+
27
+ writeFromTemplate(
28
+ path.join(getProjectRoot(), getSetting('paths.updates')),
29
+ `upgrade-${version}.php`,
30
+ 'files/update.php',
31
+ data,
32
+ options.force
33
+ )
31
34
  }
@@ -1,4 +1,3 @@
1
-
2
1
  /**
3
2
  * Node Dependencies
4
3
  */
@@ -7,25 +6,24 @@ import path from 'path';
7
6
  /**
8
7
  * Internal Dependencies
9
8
  */
10
- import { getSetting, writeFile, heading, filenameToHeading, getProjectRoot, msgErrorTitle, getTemplateFile, compileTemplate } from "../utilities/index.js";
9
+ import { getSetting, heading, filenameToHeading, getProjectRoot, writeFromTemplate } from "../utilities/index.js";
11
10
 
12
- export default (name, header) => {
11
+ export default (name, header, options = {}) => {
13
12
  heading('Creating view file...')
14
13
 
15
- try {
16
- const paths = name.toLowerCase().split('/')
17
- const filename = paths.pop() + '.php';
18
- const folder = path.join(getProjectRoot(), getSetting('paths.views'), paths.join('/'));
19
-
20
- // Data
21
- const data = getSetting();
22
- data.heading = header || filenameToHeading(filename) + ' template file';
14
+ const dest = path.join( getProjectRoot(), getSetting( 'paths.views' ), name.toLowerCase().replaceAll( '\\', '/' ) ) + '.php'
15
+ const filename = path.basename( dest )
16
+ const headingText = header || filenameToHeading(filename) + ' template file'
23
17
 
24
- const content = compileTemplate(getTemplateFile('files/view.php'), data);
25
- writeFile(folder, filename, content);
26
- }
27
- catch (err) {
28
- msgErrorTitle('We failed!!!');
29
- throw err;
30
- }
18
+ writeFromTemplate(
19
+ path.dirname( dest ),
20
+ filename,
21
+ 'files/view.php',
22
+ {
23
+ ...getSetting(),
24
+ heading: headingText,
25
+ description: headingText,
26
+ },
27
+ options.force
28
+ )
31
29
  }
@@ -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
  }