@ideasonpurpose/build-tools-wordpress 2.1.5 → 2.1.7

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
@@ -4,6 +4,20 @@ All notable changes to this project will be documented in this file. Dates are d
4
4
 
5
5
  Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
6
6
 
7
+ #### v2.1.6
8
+
9
+ > 1 May 2025
10
+
11
+ - refactor tokenizeHTML, bug fixes, tests
12
+ - typo
13
+
14
+ #### v2.1.5
15
+
16
+ > 17 April 2025
17
+
18
+ - escape capture group replacement strings when unTokenizing, plus tests
19
+ - change dev site reporter emoji
20
+
7
21
  #### v2.1.4
8
22
 
9
23
  > 16 April 2025
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @ideasonpurpose/build-tools-wordpress
2
2
 
3
- #### Version 2.1.5
3
+ #### Version 2.1.7
4
4
 
5
5
  [![NPM Version](https://img.shields.io/npm/v/%40ideasonpurpose%2Fbuild-tools-wordpress?logo=npm)](https://www.npmjs.com/package/@ideasonpurpose/build-tools-wordpress)
6
6
  [![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/ideasonpurpose/build-tools-wordpress/npm-publish.yml?logo=github&logoColor=white)](https://github.com/ideasonpurpose/build-tools-wordpress#readme)
@@ -23,16 +23,26 @@ const phpOptions = prettierConfig.overrides.find(
23
23
  (o) => o.files === "*.php",
24
24
  )?.options;
25
25
 
26
+ const isInTag = (html, offset) => {
27
+ if (offset === 0) return false;
28
+
29
+ for (let i = offset - 1; i >= 0; i--) {
30
+ if (html[i] === "<") {
31
+ return true;
32
+ } else if (html[i] === ">") {
33
+ return false;
34
+ }
35
+ }
36
+ return false;
37
+ };
38
+
26
39
  /**
27
- * Replaces PHP Code Blocks with tokens, returns tokenized HTML and an object containing
28
- * PHP Code Blocks
40
+ * Replaces all PHP Code Blocks with iterated tokens.
29
41
  *
30
- * For code blocks between tags (bounded by > & <) tokens will be self-closing HTML
31
- * tags, similar to this: <php_14______ /> PHP Code blocks at the beginning and end of
32
- * the file will be tokenized as self-closing if they preceed or follow an HTML tag.
42
+ * Code Blocks inside HTML tags will be replaced with attribute-safe tokens: _php_4____
43
+ * All other Code Blocks will be replaced with tag-shaped tokens: <php_4___ />
33
44
  *
34
- * All other PHP Code Blocks are represented as HTML attribute-safe padded strings, up
35
- * to 80 characters long.
45
+ * Tokens will match the length of their Code Blocks up to 80 characters.
36
46
  *
37
47
  * NOTE: Because Prettier's HTML formatter will always add a space before self-closing
38
48
  * tags' closing slash, we just include the space in the token to prevent it from
@@ -40,9 +50,28 @@ const phpOptions = prettierConfig.overrides.find(
40
50
  * to unTokenizeHTML().
41
51
  */
42
52
  export function tokenizeHTML(htmlContent) {
53
+ let tokenizedHTML = "";
43
54
  const phpCodeBlocks = {};
44
55
  let tokenCount = 0;
45
56
 
57
+ /**
58
+ * Check previous content for a '>' or '<' then return either an attribute-safe
59
+ * token: _php_4____ or a tag-shaped token: <php_4___ />
60
+ *
61
+ * NOTE: This uses tokenCount from the enclosing scope
62
+ */
63
+ const tokenizeCodeBlock = (phpCodeBlock, prevContent) => {
64
+ let start = "<";
65
+ let end = " />";
66
+ if (isInTag(prevContent, prevContent.length)) {
67
+ start = "_";
68
+ end = "___";
69
+ }
70
+
71
+ const codeLength = Math.min(phpCodeBlock.length, 80) - end.length;
72
+ return `${start}php_${tokenCount++}__`.padEnd(codeLength, "_") + end;
73
+ };
74
+
46
75
  // const pattern = /<\?(?:php|=)[\s\S]*?\?>/gs;
47
76
  // const pattern =
48
77
  // /(?<before>(?:[^\s]|\s|^)\s*)(?<php><\?(?:php|=).*?(?:\?>|$))(?<after>(?:\s*)[^\s]|$)/gs;
@@ -51,45 +80,28 @@ export function tokenizeHTML(htmlContent) {
51
80
  // // const pattern = /([^\s]+)\s*(<\?(?:php|=).*?(?:\?>|$))\s*([^\s]*)/gms;
52
81
  // const pattern =
53
82
  // /([^\s]?\s*)?(<\?(?:php|=).*?(?:\?>|$))((?:\s*)[^\s]|$)/gms;
54
- const pattern =
55
- /(?<=((?:[^\s]|\s|^)\s*))(<\?(?:php|=).*?\?>)(?=((?:\s*)[^\s]|$))/gms;
56
-
57
- let tokenizedHTML = htmlContent.replace(
58
- pattern,
59
- (string, before, phpCodeBlock, after, offset) => {
60
- const start = [">", ""].includes(before.trim()) ? "<" : "_";
61
- const end = ["<", ""].includes(after.trim()) ? " />" : "___";
62
-
63
- // end-pad the token to the length of the span, up to 80 characters
64
- const codeLength = Math.min(phpCodeBlock.length, 80 - end.length);
65
- const token =
66
- `${start}php_${tokenCount++}__`.padEnd(codeLength, "_") + end;
67
- phpCodeBlocks[token] = phpCodeBlock;
68
-
69
- return token;
70
- },
71
- );
83
+ // const pattern =
84
+ // /(?<=((?:[^\s]|\s|^)\s*))(<\?(?:php|=).*?\?>)(?=((?:\s*)[^\s]|$))/gms;
85
+ // try removing look ahead/behind
86
+ const pattern = /(<\?(?:php|=).*?\?>)/gms;
72
87
 
73
- /**
74
- * special case followup for open-ended PHP tags at the end of the document
75
- * TODO: Merge this back up into a single pattern
76
- * Q. Is the 'm' flag breaking the meaning of ^ and $?
77
- */
78
- tokenizedHTML = tokenizedHTML.replace(
79
- /(?<=((?:[^\s]|\s|^)\s*))(<\?(?:php|=).*$)/gms,
88
+ // const regex = new RegExp(/<\?(?:php|=).*?\?>/, "gs");
89
+ // Trying to capture open-ended PHP codeBlocks in a single regexp
90
+ const regex = new RegExp(/<\?(?:php|=).*?(?:\?>|$)/, "gs");
80
91
 
81
- (string, before, phpCodeBlock, offset) => {
82
- const start = [">", ""].includes(before.trim()) ? "<" : "_";
83
- const end = start === "<" ? " />" : "___";
92
+ let match;
93
+ let token;
94
+ let lastIndex = 0;
95
+ while ((match = regex.exec(htmlContent)) !== null) {
96
+ tokenizedHTML += htmlContent.slice(lastIndex, match.index);
84
97
 
85
- const codeLength = Math.min(phpCodeBlock.length, 80 - end.length);
86
- const token =
87
- `${start}php_${tokenCount++}__`.padEnd(codeLength, "_") + end;
88
- phpCodeBlocks[token] = phpCodeBlock;
98
+ token = tokenizeCodeBlock(match[0], tokenizedHTML);
99
+ phpCodeBlocks[token] = match[0];
100
+ tokenizedHTML += token;
89
101
 
90
- return token;
91
- },
92
- );
102
+ lastIndex = match.index + match[0].length;
103
+ }
104
+ tokenizedHTML += htmlContent.slice(lastIndex);
93
105
 
94
106
  return { tokenizedHTML, phpCodeBlocks };
95
107
  }
@@ -128,12 +128,13 @@ export default async (env) => {
128
128
  loader: "css-loader",
129
129
  options: {
130
130
  import: false, // imports already handled by Sass or PostCSS
131
- // sourceMap: !isProduction,
131
+ sourceMap: !isProduction,
132
132
  },
133
133
  },
134
134
  {
135
135
  loader: "postcss-loader",
136
136
  options: {
137
+ sourceMap: !isProduction,
137
138
  postcssOptions: {
138
139
  plugins: isProduction
139
140
  ? [
@@ -144,13 +145,6 @@ export default async (env) => {
144
145
  },
145
146
  },
146
147
  },
147
- // {
148
- // loader: "resolve-url-loader",
149
- // options: {
150
- // // sourceMap: true,
151
- // // debug: true,
152
- // },
153
- // },
154
148
  {
155
149
  loader: "sass-loader",
156
150
  options: {
@@ -27,7 +27,7 @@ export class AfterDoneReporterPlugin {
27
27
  * NOTE: As of webpack-dev-server vXX.XX.XXX, auto-assigned ports are
28
28
  * not propagated into the compiler.options.devServer object.
29
29
  *
30
- * As a workaround, this relies on the follining line being added to
30
+ * As a workaround, this relies on the following line being added to
31
31
  * the webpack.config file's devServer.setupMiddlewares method:
32
32
  *
33
33
  * devServer.compiler.options.devServer.port = devServer.options.port;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ideasonpurpose/build-tools-wordpress",
3
- "version": "2.1.5",
3
+ "version": "2.1.7",
4
4
  "description": "Build scripts and dependencies for IOP's WordPress development environments.",
5
5
  "homepage": "https://github.com/ideasonpurpose/build-tools-wordpress#readme",
6
6
  "bugs": {
@@ -39,30 +39,30 @@
39
39
  "@rollup/plugin-json": "^6.1.0",
40
40
  "@rollup/plugin-node-resolve": "^16.0.1",
41
41
  "@svgr/webpack": "^8.1.0",
42
- "@wordpress/dependency-extraction-webpack-plugin": "^6.22.0",
42
+ "@wordpress/dependency-extraction-webpack-plugin": "^6.24.0",
43
43
  "ansi-html": "^0.0.9",
44
44
  "archiver": "^7.0.1",
45
45
  "auto-changelog": "^2.5.0",
46
46
  "autoprefixer": "^10.4.21",
47
47
  "babel-loader": "^10.0.0",
48
- "caniuse-lite": "^1.0.30001713",
48
+ "caniuse-lite": "^1.0.30001718",
49
49
  "chalk": "^5.4.1",
50
- "chalk-cli": "^5.0.1",
50
+ "chalk-cli": "^6.0.0",
51
51
  "classnames": "^2.5.1",
52
52
  "cli-truncate": "^4.0.0",
53
53
  "copy-webpack-plugin": "^13.0.0",
54
54
  "cosmiconfig": "^9.0.0",
55
55
  "cross-env": "^7.0.3",
56
56
  "css-loader": "^7.1.2",
57
- "cssnano": "^7.0.6",
57
+ "cssnano": "^7.0.7",
58
58
  "dotenv": "^16.5.0",
59
59
  "esbuild-loader": "^4.3.0",
60
- "eslint": "^9.24.0",
60
+ "eslint": "^9.27.0",
61
61
  "filesize": "^10.1.6",
62
62
  "fs-extra": "^11.3.0",
63
63
  "globby": "^14.1.0",
64
64
  "http-proxy": "^1.18.1",
65
- "humanize-duration": "^3.32.1",
65
+ "humanize-duration": "^3.32.2",
66
66
  "image-minimizer-webpack-plugin": "^4.1.3",
67
67
  "is-text-path": "^3.0.0",
68
68
  "lodash": "^4.17.21",
@@ -74,17 +74,17 @@
74
74
  "pretty-hrtime": "^1.0.3",
75
75
  "read-package-up": "^11.0.0",
76
76
  "replacestream": "^4.0.3",
77
- "sass-embedded": "^1.86.3",
77
+ "sass-embedded": "^1.89.0",
78
78
  "sass-loader": "^16.0.5",
79
- "semver": "^7.7.1",
80
- "sharp": "^0.34.1",
81
- "sort-package-json": "^3.0.0",
79
+ "semver": "^7.7.2",
80
+ "sharp": "^0.34.2",
81
+ "sort-package-json": "^3.2.1",
82
82
  "string-length": "^6.0.0",
83
83
  "style-loader": "^4.0.0",
84
84
  "svgo": "^3.3.2",
85
85
  "svgo-loader": "^4.0.0",
86
86
  "version-everything": "^0.11.4",
87
- "webpack": "^5.99.5",
87
+ "webpack": "^5.99.9",
88
88
  "webpack-bundle-analyzer": "^4.10.2",
89
89
  "webpack-dev-middleware": "^7.4.2",
90
90
  "webpack-dev-server": "^5.2.1"
@@ -93,8 +93,8 @@
93
93
  "webpack-cli": "^6.0.1"
94
94
  },
95
95
  "devDependencies": {
96
- "@vitest/coverage-v8": "^3.1.1",
97
- "vitest": "^3.1.1"
96
+ "@vitest/coverage-v8": "^3.1.4",
97
+ "vitest": "^3.1.4"
98
98
  },
99
99
  "version-everything": {
100
100
  "files": [
@@ -0,0 +1,3 @@
1
+ <i aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa="<?= a('a') ?>"><?= b('b') ?> : <?= c('c') ?></i>
2
+
3
+ <div r="<?= a('a') ?>"><?= b('b') ?> : <?= c('c') ?></div>
@@ -55,9 +55,11 @@ describe("HTML-PHP Prettier", () => {
55
55
  )
56
56
  ).toString();
57
57
 
58
- const { phpCodeBlocks: codeBlocks } = tokenizeHTML(input);
58
+ const { tokenizedHTML, phpCodeBlocks } = tokenizeHTML(input);
59
59
 
60
- const tokens = Object.keys(codeBlocks);
60
+ // console.log({input, tokenizedHTML})
61
+
62
+ const tokens = Object.keys(phpCodeBlocks);
61
63
 
62
64
  expect(tokens[0]).toMatch(/^_php_\d+_*$/);
63
65
  expect(tokens[1]).toMatch(/^_php_\d+_*$/);
@@ -85,4 +87,52 @@ describe("HTML-PHP Prettier", () => {
85
87
  expect(formattedContent).toContain("'b $&'");
86
88
  expect(formattedContent).toContain("'$0 $1 $2 $3'");
87
89
  });
90
+
91
+ /**
92
+ * Not sure what's going on here, but this breaks and returns with the last replacement token still in place.
93
+ * Something to do with the overall length, if the aaa...aaa attribute is shortened it formats correctly.
94
+ *
95
+ * Something about this file, likely the length, is causing it to fail to find
96
+ * and restore the last PHP token. Trying to format this:
97
+ *
98
+ *
99
+ * <i aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa="<?= a('a') ?>"><?= b('b') ?> : <?= c('c') ?></i>
100
+ *
101
+ * ...ends up returning something like this:
102
+ *
103
+ * <i aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa="<?= a('a') ?>"
104
+ * ><?= b('b') ?> : _php_3_______
105
+ * /></i>
106
+ *
107
+ * It seems to have something to do with HTML whitespace preservation, I can't get this to
108
+ * happen with a div
109
+ */
110
+ test("whitespace orphaned tokens", async () => {
111
+ const input = (
112
+ await readFile(
113
+ "./test/fixtures/format-php-prettier/length-orphaned-token-bug.php",
114
+ )
115
+ ).toString();
116
+
117
+ const { tokenizedHTML, phpCodeBlocks } = tokenizeHTML(input);
118
+
119
+ const formattedContent = unTokenizeHTML(tokenizedHTML, phpCodeBlocks);
120
+
121
+ // console.log(input, tokenizedHTML, phpCodeBlocks, formattedContent);
122
+
123
+ expect(formattedContent).toContain("<?= c('c') ?>");
124
+ });
125
+
126
+ test("tokenization error (drill down from whitespace orphaned tokens)", async () => {
127
+ const input = "<?= b('b') ?> : <?= c('c') ?>";
128
+ const { tokenizedHTML, phpCodeBlocks } = tokenizeHTML(input);
129
+
130
+ // console.log({input, tokenizedHTML, phpCodeBlocks});
131
+
132
+
133
+ expect(tokenizedHTML).toContain("<php_0____ />");
134
+ expect(tokenizedHTML).toContain("<php_1____ />");
135
+ expect(phpCodeBlocks).toHaveProperty("<php_0____ />");
136
+ expect(phpCodeBlocks).toHaveProperty("<php_1____ />");
137
+ });
88
138
  });