@penskemediacorp/twig-to-php-parser 1.10.0-alpha.0 → 1.26.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.
Files changed (35) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +51 -4
  3. package/__tests__/fixtures/larva.config.js +7 -0
  4. package/__tests__/fixtures/src/patterns/04-components/c-byline/c-byline.twig +1 -1
  5. package/__tests__/fixtures/src/patterns/04-components/c-heading/c-heading.twig +0 -1
  6. package/__tests__/fixtures/src/patterns/04-components/c-logo/c-logo.twig +3 -0
  7. package/__tests__/fixtures/src/patterns/08-modules/array-key-in-loop/array-key-in-loop.twig +11 -0
  8. package/__tests__/fixtures/src/patterns/08-modules/escaped-output-in-loop/escaped-output-in-loop.twig +5 -0
  9. package/__tests__/fixtures/src/patterns/08-modules/html-tag-interpolation/html-tag-interpolation.twig +7 -0
  10. package/__tests__/fixtures/src/patterns/08-modules/trending/trending.twig +1 -0
  11. package/__tests__/fixtures/src/patterns/99-objects/o-nav/o-nav.twig +4 -0
  12. package/__tests__/fixtures/template-parts/.gitkeep +1 -0
  13. package/__tests__/fixtures/template-parts-expected/patterns/components/c-button.php +7 -1
  14. package/__tests__/fixtures/template-parts-expected/patterns/components/c-byline.php +7 -1
  15. package/__tests__/fixtures/template-parts-expected/patterns/components/c-heading.php +7 -2
  16. package/__tests__/fixtures/template-parts-expected/patterns/components/c-logo.php +12 -0
  17. package/__tests__/fixtures/template-parts-expected/patterns/components/c-nav-link.php +8 -2
  18. package/__tests__/fixtures/template-parts-expected/patterns/modules/array-key-in-loop.php +20 -0
  19. package/__tests__/fixtures/template-parts-expected/patterns/modules/breadcrumbs.php +8 -2
  20. package/__tests__/fixtures/template-parts-expected/patterns/modules/escaped-output-in-loop.php +5 -0
  21. package/__tests__/fixtures/template-parts-expected/patterns/modules/html-tag-interpolation.php +16 -0
  22. package/__tests__/fixtures/template-parts-expected/patterns/modules/trending.php +10 -3
  23. package/__tests__/fixtures/template-parts-expected/patterns/objects/o-crap.php +7 -1
  24. package/__tests__/fixtures/template-parts-expected/patterns/objects/o-nav.php +12 -2
  25. package/__tests__/fixtures/template-parts-expected/patterns/objects/o-story-list.php +8 -2
  26. package/__tests__/parser-methods.test.js +107 -32
  27. package/__tests__/twig-to-php-parser.test.js +44 -28
  28. package/index.js +140 -49
  29. package/lib/getConfig.js +48 -0
  30. package/lib/twig-to-php-parser.php +218 -61
  31. package/package.json +3 -3
  32. package/.DS_Store +0 -0
  33. package/__tests__/.DS_Store +0 -0
  34. package/__tests__/fixtures/src/patterns/.DS_Store +0 -0
  35. package/__tests__/fixtures/template-parts/some-template.php +0 -3
package/index.js CHANGED
@@ -1,81 +1,172 @@
1
1
  const execPhp = require( 'exec-php' );
2
2
  const path = require( 'path' );
3
3
  const chalk = require( 'chalk' );
4
- const getAppConfiguration = require( '@penskemediacorp/larva' ).config;
5
- const RELATIVE_OUPUT_PATH = '../template-parts/patterns'; // Not permitted to override this because it will break the include paths.
4
+ const getAppConfiguration = require( './lib/getConfig' );
5
+ const config = getAppConfiguration( 'parser' );
6
6
 
7
7
  /**
8
8
  * Twig to PHP Parser
9
- *
10
- * @param {string} twigDirPath Absolute path to Twig patterns
11
- * @param {string} phpDirPath Absolute path to PHP output
12
- * @param {object} config Containing
9
+ *
10
+ * @param {Object} config Optional object containing configuration. See the
11
+ * package README for supported configuration.
13
12
  */
14
13
 
15
- function twigToPhpParser( twigDirPath, phpDirPath, config = {} ) {
16
-
17
- return new Promise( ( resolve, reject ) => {
18
-
19
- execPhp( path.resolve( __dirname, './lib/twig-to-php-parser.php' ), config.phpBinaryPath, function( error, php, output ) {
14
+ function twigToPhpParser( config = {} ) {
15
+ // TODO: We need some kind of wrapper here for config...or better:
16
+ // move this to pmc-plugins/pmc-larva and scrap this awkward JS
17
+ // execPhp wrapper situation, and use Larva\Config for this config.
18
+ let twigDir = path.join( process.cwd(), './src/patterns' );
19
+ let phpDir = path.join( process.cwd(), '../template-parts/patterns' );
20
+ let isUsingPlugin = false;
21
+
22
+ if ( 'undefined' !== typeof config.twigDir ) {
23
+ if ( config.twigDir.startsWith( '.' ) ) {
24
+ twigDir = path.join( process.cwd(), config.twigDir );
25
+ } else {
26
+ twigDir = config.twigDir;
27
+ }
28
+ } else if ( 'undefined' !== typeof config.relativeSrcOverride ) {
29
+ if ( config.relativeSrcOverride.startsWith( '/' ) ) {
30
+ twigDir = config.relativeSrcOverride;
31
+ } else {
32
+ twigDir = path.join( process.cwd(), config.relativeSrcOverride );
33
+ }
34
+ }
20
35
 
21
- if ( error ) {
22
- reject( error );
23
- }
36
+ if ( 'undefined' !== typeof config.isUsingPlugin ) {
37
+ isUsingPlugin = config.isUsingPlugin;
38
+ }
24
39
 
25
- php.twig_to_php_parser( twigDirPath, phpDirPath, function( error, result, output, printed ) {
40
+ if ( 'undefined' !== typeof config.phpDir ) {
41
+ phpDir = config.phpDir;
42
+ }
26
43
 
44
+ return new Promise( ( resolve, reject ) => {
45
+ execPhp(
46
+ path.resolve( __dirname, './lib/twig-to-php-parser.php' ),
47
+ config.phpBinaryPath,
48
+ function ( error, php, output ) {
27
49
  if ( error ) {
28
50
  reject( error );
29
51
  }
30
52
 
31
- resolve( result );
32
-
33
- });
34
-
35
- });
36
-
37
- });
38
-
39
- };
40
-
41
- function parseIncludePath( twigIncludeStr, patternName, dataName, config = {} ) {
42
-
53
+ php.twig_to_php_parser(
54
+ twigDir,
55
+ phpDir,
56
+ isUsingPlugin,
57
+ function ( error, result, output, printed ) {
58
+ if ( error ) {
59
+ reject( error );
60
+ }
61
+
62
+ resolve( result );
63
+ }
64
+ );
65
+ }
66
+ );
67
+ } );
68
+ }
69
+
70
+ function parseIncludePath(
71
+ twigIncludeStr,
72
+ patternName,
73
+ dataName,
74
+ isUsingPlugin = false
75
+ ) {
43
76
  return new Promise( ( resolve, reject ) => {
77
+ execPhp(
78
+ path.resolve( __dirname, './lib/twig-to-php-parser.php' ),
79
+ config.phpBinaryPath,
80
+ function ( error, php, output ) {
81
+ if ( error ) {
82
+ reject( error );
83
+ }
44
84
 
45
- execPhp( path.resolve( __dirname, './lib/twig-to-php-parser.php' ), config.phpBinaryPath, function( error, php, output ) {
46
-
47
- if ( error ) {
48
- reject( error );
85
+ php.parse_include_path(
86
+ twigIncludeStr,
87
+ patternName,
88
+ dataName,
89
+ isUsingPlugin,
90
+ function ( error, result, output, printed ) {
91
+ if ( error ) {
92
+ reject( error );
93
+ }
94
+
95
+ resolve( result );
96
+ }
97
+ );
49
98
  }
99
+ );
100
+ } );
101
+ }
50
102
 
51
- php.parse_include_path( twigIncludeStr, patternName, dataName, function( error, result, output, printed ) {
52
-
103
+ function parseSvgPath( twigSvgIncludeStr, svgName, isUsingPlugin = false ) {
104
+ return new Promise( ( resolve, reject ) => {
105
+ execPhp(
106
+ path.resolve( __dirname, './lib/twig-to-php-parser.php' ),
107
+ config.phpBinaryPath,
108
+ function ( error, php, output ) {
53
109
  if ( error ) {
54
110
  reject( error );
55
111
  }
56
112
 
57
- resolve( result );
58
-
59
- });
113
+ php.parse_svg_path(
114
+ twigSvgIncludeStr,
115
+ svgName,
116
+ isUsingPlugin,
117
+ function ( error, result, output, printed ) {
118
+ if ( error ) {
119
+ reject( error );
120
+ }
121
+
122
+ resolve( result );
123
+ }
124
+ );
125
+ }
126
+ );
127
+ } );
128
+ }
60
129
 
61
- });
130
+ function parseWpAction( twigMarkup ) {
131
+ return new Promise( ( resolve, reject ) => {
132
+ execPhp(
133
+ path.resolve( __dirname, './lib/twig-to-php-parser.php' ),
134
+ config.phpBinaryPath,
135
+ function ( error, php, output ) {
136
+ if ( error ) {
137
+ reject( error );
138
+ }
62
139
 
63
- });
140
+ php.parse_wp_action(
141
+ twigMarkup,
142
+ function ( error, result, output, printed ) {
143
+ if ( error ) {
144
+ reject( error );
145
+ }
64
146
 
65
- };
147
+ resolve( result );
148
+ }
149
+ );
150
+ }
151
+ );
152
+ } );
153
+ }
66
154
 
67
155
  module.exports = {
68
- twigToPhpParser: twigToPhpParser,
156
+ twigToPhpParser,
69
157
  methods: {
70
- parseIncludePath: parseIncludePath
158
+ parseIncludePath,
159
+ parseSvgPath,
160
+ parseWpAction,
71
161
  },
72
162
  run: () => {
73
- let config = getAppConfiguration( 'parser' );
74
- let relativeSrcPath = config.relativeSrcOverride || './src/patterns';
75
-
76
- twigToPhpParser( path.resolve( process.cwd(), relativeSrcPath ), path.resolve( process.cwd(), RELATIVE_OUPUT_PATH ), config )
77
- .catch( e => console.log( e ) ) // PHP errors
78
- .then( result => console.log( chalk.green( 'Completed parsing Twig templates to PHP.' ) ) )
79
- .catch( e => console.log( e ) );
80
- }
163
+ twigToPhpParser( config )
164
+ .catch( ( e ) => console.log( e ) ) // PHP errors
165
+ .then( ( result ) =>
166
+ console.log(
167
+ chalk.green( 'Completed parsing Twig templates to PHP.' )
168
+ )
169
+ )
170
+ .catch( ( e ) => console.log( e ) );
171
+ },
81
172
  };
@@ -0,0 +1,48 @@
1
+ const chalk = require( 'chalk' );
2
+ const path = require( 'path' );
3
+
4
+ /**
5
+ * Get Larva Configuration.
6
+ *
7
+ * Find larva.config.js according to the current working directory.
8
+ * For consuming projects, this will be theme/assets/, and for the
9
+ * mono-repo, it will be packages/larva.
10
+ *
11
+ * For test env, the CWD is overridden to the local directory for fixtures.
12
+ * (this is a problem for writing project-level tests, but for a later time.)
13
+ *
14
+ * Note that this is copied from packages/larva/lib in order to
15
+ * remove dependency.
16
+ *
17
+ * @param tool string that indicates a specific configuration property, e.g. patterns.
18
+ *
19
+ * @return value of the specified key.
20
+ */
21
+
22
+ module.exports = function getAppConfiguration( key ) {
23
+ try {
24
+ let appRoot = process.cwd();
25
+
26
+ if ( 'test' === process.env.NODE_ENV ) {
27
+ appRoot = path.join( __dirname, '../__tests__/fixtures' );
28
+ }
29
+
30
+ const config = require( `${ appRoot }/larva.config.js` )[ key ];
31
+
32
+ if ( undefined === config ) {
33
+ throw new Error(
34
+ `Configuration for \`${ key }\` is required in larva.config.js. \nPlease refer to the Larva's docs for adding configuration: https://github.com/penske-media-corp/pmc-larva`
35
+ );
36
+ }
37
+
38
+ return config;
39
+ } catch ( error ) {
40
+ console.error(
41
+ chalk.red(
42
+ chalk.bold(
43
+ 'There is no larva.config.js in this directory.\n'
44
+ ) + error
45
+ )
46
+ );
47
+ }
48
+ };
@@ -1,80 +1,94 @@
1
1
  <?php
2
-
3
2
  /**
4
3
  * Twig to PHP Patterns Parser
5
- *
4
+ *
6
5
  * This will slowly be broken up into smaller, testable functions that
7
6
  * can be refactored into node if we choose to go that route.
8
- *
9
- * @since 06/26/2019
10
- * @author Lara Schenck and Amit Sannad
7
+ *
8
+ * @since 06/26/2019
9
+ * @author Lara Schenck and Amit Sannad
10
+ * @package pmc-lavra
11
11
  */
12
12
 
13
- function twig_to_php_parser( $patterns_dir_path, $template_dir_path ) {
14
- $twig_files = [];
13
+ /**
14
+ * Function parser to convert twig to php.
15
+ *
16
+ * @param string $patterns_dir_path patterns path.
17
+ * @param string $template_dir_path template directory path.
18
+ * @param bool $is_using_plugin whether or not plugin is in use.
19
+ * @return void
20
+ */
21
+ function twig_to_php_parser( $patterns_dir_path, $template_dir_path, $is_using_plugin ) {
22
+ $twig_files = [];
15
23
 
16
24
  $twig_dir_iterator = new \RecursiveDirectoryIterator( $patterns_dir_path );
17
25
  $recirsive_dir_iterator = new \RecursiveIteratorIterator( $twig_dir_iterator );
18
26
  $all_twig_files = new \RegexIterator( $recirsive_dir_iterator, '/\.twig/' );
19
27
 
20
- // Get all twig twig_files to generate template for
28
+ // Get all twig twig_files to generate template for.
21
29
  foreach ( $all_twig_files as $info ) {
22
30
  $twig_files[] = $info->getPathname();
23
31
  }
24
32
 
25
33
  foreach ( $twig_files as $twig_file ) {
26
34
 
27
- // All components of path in array split by /
35
+ // All components of path in array split by /.
28
36
  $twig_path_arr = explode( '/', $twig_file );
29
-
30
- // Get last three parts of the file that includes directory
37
+
38
+ // Get last three parts of the file that includes directory.
31
39
  $path_info = array_slice( $twig_path_arr, count( $twig_path_arr ) - 3, 3 );
32
40
 
33
41
  // Generate directory where we wanna write PHP template. Remove any thing thats not alpha string i.e. remove specials chars including numbers
34
- // "/patterns/06-components/c-title/c-title.twig" => "/template-parts/patterns/components/c-title.php"
42
+ // "/patterns/06-components/c-title/c-title.twig" => "/template-parts/patterns/components/c-title.php".
35
43
  $template_dir = $template_dir_path . '/' . preg_replace( '/[^A-Za-z]/', '', $path_info[0] );
36
44
 
37
- // Generate path where we wanna write php template
45
+ // Generate path where we wanna write php template.
38
46
  $path = $template_dir . '/' . str_replace( '.twig', '.php', $path_info[2] );
39
47
 
48
+ debug_log( sprintf( 'Source: %s', $twig_file ) );
49
+ debug_log( sprintf( 'Target: %s', $path ) );
50
+
40
51
  if ( file_exists( $path ) ) {
41
52
 
42
- // Get file time of template and twig
53
+ // Get file time of template and twig.
43
54
  $fmtime_twig = filemtime( $twig_file );
44
55
  $fmtime_template = filemtime( $path );
45
56
 
46
- // Compare twig file time to php template filetime. If twig file is newer than php template then overwrite the php template
47
- // This is to make sure we are not unnecessarily overwriting the php template
57
+ // Compare twig file time to php template filetime. If twig file is newer than php template then overwrite the php template.
58
+ // This is to make sure we are not unnecessarily overwriting the php template.
48
59
  if ( $fmtime_twig < $fmtime_template ) {
49
-
60
+ debug_log( sprintf( " -> Skipped, twig timestamp: %s, template timestamp: %s\n", $fmtime_twig, $fmtime_template ) );
50
61
  // NOTE: Ignoring this just to fixing pipeline, however it's showing 100% coverage in local.
51
62
  // Ignoring this line is suggested by teach lead.
52
63
  continue; // @codeCoverageIgnore
53
64
  }
54
-
55
65
  }
56
66
 
57
67
  // Get matches for `{% if VARIABLE %}`
58
68
  // If condition regex.
59
- // https://regex101.com/r/Lnoaau/1
60
- $if_condition_regex = '/({% if\s*)(\w*)(\s*%})/';
69
+ // https://regex101.com/r/BwA2r0/1.
70
+ $if_condition_regex = '/({% if\s*)(\w*\[\'\w+\'\]|\w+)(\s*%})/';
61
71
 
62
- // Get matches for `{% for item in VARIABLE`
63
- // https://regex101.com/r/lceCnz/2/
64
- $loop_regex = '/({% for item in\s*)(\w*)/';
72
+ // Get matches for `{% for item in VARIABLE`.
73
+ // https://regex101.com/r/ijLuIA/3.
74
+ $loop_regex = '/({% for (\w*\[\'\w+\'\]|\w+) in\s*)(\w*\[\'\w+\'\]|\w+)/';
65
75
 
66
- // Get matches for {{ name }}
67
- // https://regex101.com/r/ACN0rE/1
68
- $mustache_regex = '/({{\s*)(\w*)(\s*}})/';
76
+ // Get matches for {{ name }}, {{ name|filter }}, {{ name['item'] }}
77
+ // https://regex101.com/r/ACN0rE/5.
78
+ $mustache_regex = '/({{\s*)(\w*?\[?\'?\w*\'?\]?\|?\w*)(\s*}})/';
69
79
 
70
- // Get matches for {% include "path/c-element.twig" with data %}
71
- // https://regex101.com/r/ns5kBR/2
72
- $include_regex = '/({%\sinclude ")(.*[c-|o-|l-].*\/)(.*)(.twig)(" with )(\w*)(.*\s%})/';
80
+ // Get matches for {% include "path/c-element.twig" with data['some_data'] %}
81
+ // https://regex101.com/r/ns5kBR/3.
82
+ $include_regex = '/({%\sinclude ")(.*[c-|o-|l-].*\/)(.*)(.twig)(" with )(\w*?\[?\'?\w*\'?\]?\|?\w*)(\s%})/';
73
83
 
74
- $twig_markup = \file_get_contents( $twig_file );
84
+ // Get matches for {% include '/path/here/' ~ variable_svg ~ '.svg' %}
85
+ // https://regex101.com/r/pdleQb/3.
86
+ $svg_regex = '/({%\sinclude\s.*)(\s?\~\s?)(\S*)(\s?\~\s?)(.*\s.*)(%})/';
87
+
88
+ $twig_markup = \file_get_contents( $twig_file ); // phpcs:ignore
75
89
 
76
90
  $general_replacers = [
77
- '{#' => '<?php /*' . "\n",
91
+ '{#' => '<?php' . "\n/**" . "\n",
78
92
  '#}' => '*/' . "\n" . '?>',
79
93
  '{% endif %}' => '<?php } ?>',
80
94
  '{% else %}' => '<?php } else { ?>',
@@ -88,12 +102,14 @@ function twig_to_php_parser( $patterns_dir_path, $template_dir_path ) {
88
102
  preg_match_all( $loop_regex, $twig_markup, $loop_matches );
89
103
  preg_match_all( $mustache_regex, $twig_markup, $mustache_matches );
90
104
  preg_match_all( $include_regex, $twig_markup, $include_matches );
105
+ preg_match_all( $svg_regex, $twig_markup, $svg_matches );
91
106
 
92
107
  // Prepare empty arrays to gather the replacements as they are processed.
93
108
  $if_condition_replacements = [];
94
109
  $loop_replacements = [];
95
110
  $mustache_replacements = [];
96
111
  $include_replacements = [];
112
+ $svg_replacements = [];
97
113
 
98
114
  $count = 0;
99
115
  foreach ( $if_condition_matches[0] as $key => $match ) {
@@ -108,9 +124,10 @@ function twig_to_php_parser( $patterns_dir_path, $template_dir_path ) {
108
124
  $count = 0;
109
125
  foreach ( $loop_matches[0] as $key => $match ) {
110
126
 
111
- $variable_name = $loop_matches[2][ $count ];
127
+ $key_name = $loop_matches[2][ $count ];
128
+ $variable_name = $loop_matches[3][ $count ];
112
129
 
113
- $loop_replacements[ $count ] = '<?php foreach ( $' . $variable_name . ' ?? [] as $item';
130
+ $loop_replacements[ $count ] = '<?php foreach ( $' . $variable_name . ' ?? [] as $' . $key_name;
114
131
 
115
132
  $count ++;
116
133
  }
@@ -119,11 +136,24 @@ function twig_to_php_parser( $patterns_dir_path, $template_dir_path ) {
119
136
  foreach ( $mustache_matches[0] as $key => $match ) {
120
137
 
121
138
  $variable_name = $mustache_matches[2][ $count ];
122
- $is_attr = strpos( $match, 'class' ) || strpos( $match, 'name' ) || strpos( $match, 'attr' );
123
- $is_url = strpos( $match, 'url' );
124
- $is_text = strpos( $match, 'text' );
125
- $is_data_attr = strpos( $match, 'attributes' );
126
- $is_markup = strpos( $match, 'markup' );
139
+
140
+ $is_attr = strpos( $match, '_class' ) || strpos( $match, '_name' ) || strpos( $match, '_attr' );
141
+ $is_url = strpos( $match, '_url' );
142
+ $is_text = strpos( $match, '_text' );
143
+ $is_data_attr = strpos( $match, '_attributes' );
144
+ $is_markup = strpos( $match, '_markup' );
145
+ $is_action = strpos( $match, '_wp_action' );
146
+ $has_filter = strpos( $match, '|' );
147
+
148
+ // Remove the Twig filter from the variable name.
149
+ if ( $has_filter ) {
150
+ $string_parts = explode( '|', $variable_name );
151
+ $variable_name = $string_parts[0];
152
+
153
+ if ( 'markup' === $string_parts[1] ) {
154
+ $is_markup = true;
155
+ }
156
+ }
127
157
 
128
158
  if ( ! empty( $is_url ) ) {
129
159
  $mustache_replacements[ $count ] = '<?php echo esc_url( $' . $variable_name . ' ?? \'\' ); ?>';
@@ -141,68 +171,195 @@ function twig_to_php_parser( $patterns_dir_path, $template_dir_path ) {
141
171
  $mustache_replacements[ $count ] = '<?php echo wp_kses_post( $' . $variable_name . ' ?? \'\' ); ?>';
142
172
  }
143
173
 
174
+ if ( ! empty( $is_action ) ) {
175
+ $mustache_replacements[ $count ] = '<?php do_action( \'' . $variable_name . '\' ); ?>';
176
+ }
177
+
144
178
  $count ++;
145
179
  }
146
180
 
147
181
  // Reset the count to begin accumulating $include_replacements.
148
182
  $count = 0;
149
-
150
183
  foreach ( $include_matches[0] as $include ) {
151
184
  $include_replacements[ $count ] = parse_include_path(
152
185
  $include,
153
186
  $include_matches[3][ $count ],
154
- $include_matches[6][ $count ]
187
+ $include_matches[6][ $count ],
188
+ $is_using_plugin
189
+ );
190
+ $count ++;
191
+ }
192
+
193
+ // Reset the count to begin accumulating $include_replacements.
194
+ $count = 0;
195
+ foreach ( $svg_matches[0] as $svg ) {
196
+ $svg_replacements[ $count ] = parse_svg_path(
197
+ $svg,
198
+ $svg_matches[3][ $count ],
199
+ $is_using_plugin
155
200
  );
156
201
  $count ++;
157
202
  }
158
203
 
159
204
  // Merge replacements and matches arrays.
160
- $all_matches = (array) array_merge( $mustache_matches[0], $include_matches[0], $loop_matches[0], $if_condition_matches[0] );
161
- $all_replacements = (array) array_merge( $mustache_replacements, $include_replacements, $loop_replacements, $if_condition_replacements );
205
+ $all_matches = (array) array_merge(
206
+ $mustache_matches[0],
207
+ $include_matches[0],
208
+ $loop_matches[0],
209
+ $if_condition_matches[0],
210
+ $svg_matches[0]
211
+ );
212
+
213
+ $all_replacements = (array) array_merge(
214
+ $mustache_replacements,
215
+ $include_replacements,
216
+ $loop_replacements,
217
+ $if_condition_replacements,
218
+ $svg_replacements
219
+ );
162
220
 
163
221
  // First replace matches from the regex's, then apply the general replacements.
164
222
  $twig_markup_replace_main = str_replace( $all_matches, $all_replacements, $twig_markup );
223
+ $twig_markup_replace_main = parse_wp_action( $twig_markup_replace_main );
165
224
  $twig_markup_replace_complete = str_replace( array_keys( (array) $general_replacers ), array_values( $general_replacers ), $twig_markup_replace_main );
166
225
 
167
- $php_markup = "<?php\n// This is a generated file. Refer to the relevant Twig file for adjusting this markup.\n?>\n";
226
+ $php_markup = "<?php\n/**\n * Generated file.\n *\n * Refer to the relevant Twig file for adjusting this markup.\n */\n\n?>\n\n";
168
227
  $php_markup .= $twig_markup_replace_complete;
169
228
 
170
229
  if ( ! file_exists( $template_dir ) ) {
171
230
  mkdir( $template_dir );
172
231
  }
173
232
 
174
- file_put_contents( $path, $php_markup );
233
+ file_put_contents( $path, $php_markup ); // phpcs:ignore
234
+
235
+ debug_log( " -> Template generated\n" );
175
236
 
176
237
  }
177
238
  }
178
239
 
179
240
  /**
180
241
  * Parse Twig Includes
181
- *
182
- * @param string $twig_include Full include expression
183
- * @param string $pattern_name o-nav
184
- * @param string $data_name o_nav / usually name of pattern with underscores
185
- *
242
+ *
243
+ * @param string $twig_include Full include expression.
244
+ * @param string $pattern_name o-nav.
245
+ * @param string $data_name o_nav / usually name of pattern with underscores.
246
+ * @param boolean $is_using_plugin Whether or not the plugin is in use.
247
+ *
186
248
  * @return string PMC::render_template call
187
249
  */
188
- function parse_include_path( $twig_include, $pattern_name, $data_name ) {
189
- $theme_dir = 'CHILD_THEME_PATH';
190
- $start_name = substr( $pattern_name, 0, 2 );
250
+ function parse_include_path( $twig_include, $pattern_name, $data_name, $is_using_plugin ) {
251
+
252
+ $brand_directory = 'CHILD_THEME_PATH';
253
+ $pattern_directory = '/template-parts/patterns/';
254
+ $start_name = substr( $pattern_name, 0, 2 );
255
+ $pattern_type = '';
191
256
 
192
- // 08/09/19 - Disabling larva/core theme logic
193
- // if ( strpos( $twig_include, "@larva" ) ) {
194
- // $theme_dir = 'PMC_CORE_PATH';
195
- // }
257
+ if ( strpos( $twig_include, '@larva' ) ) {
258
+ $brand_directory = 'PMC_CORE_PATH';
259
+ }
196
260
 
197
261
  if ( 'c-' === $start_name ) {
198
- $directory = 'components';
262
+ $pattern_type = 'components';
199
263
  } elseif ( 'o-' === $start_name ) {
200
- $directory = 'objects';
264
+ $pattern_type = 'objects';
201
265
  } elseif ( '-' !== substr( $pattern_name, 1, 2 ) ) { // If there is no namespace, it is a module.
202
- $directory = 'modules';
266
+ $pattern_type = 'modules';
203
267
  }
204
268
 
205
- return "<?php \PMC::render_template( " . $theme_dir . " . '/template-parts/patterns/" . $directory . "/" . $pattern_name . ".php', $" . $data_name . ', true ); ?>';
269
+ if ( $is_using_plugin ) {
270
+ return "<?php \PMC\Larva\Pattern::get_instance()->render_pattern_template( '" . $pattern_type . '/' . $pattern_name . "', $" . $data_name . ", true ); ?>";
271
+ } else {
272
+ $pattern_partial_path = $pattern_directory . $pattern_type . '/' . $pattern_name;
273
+
274
+ return '<?php \PMC::render_template( ' . $brand_directory . " . '" . $pattern_partial_path . ".php', $" . $data_name . ', true ); ?>';
275
+ }
276
+ }
277
+
278
+ /**
279
+ * Parse SVG Path
280
+ *
281
+ * @param string $twig_include Full include expression.
282
+ * @param string $svg_name Name of SVG file, excluding extension.
283
+ * @param boolean $is_using_plugin Whether or not the plugin is in use.
284
+ *
285
+ * @return string PMC::render_template call
286
+ */
287
+ function parse_svg_path( $twig_include, $svg_name, $is_using_plugin ) {
288
+ $brand_directory = 'CHILD_THEME_PATH';
289
+ $svg_directory = '/assets/build/svg/';
290
+ $start_name = substr( $svg_name, 0, 2 );
291
+
292
+ if ( true === $is_using_plugin ) {
293
+ $svg_directory = '/build/svg/';
294
+ $key_name = 'brand_directory';
295
+ $brand_directory = "\PMC\Larva\Config::get_instance()->get( '" . $key_name . "' )";
296
+ } else {
297
+ if ( strpos( $twig_include, '@larva' ) ) {
298
+ $brand_directory = 'PMC_CORE_PATH';
299
+ }
300
+ }
301
+
302
+ return "<?php \PMC::render_template( " . $brand_directory . " . '" . $svg_directory . "' . ( $" . $svg_name . " ?? '' ) . '.svg', [], true ); ?>";
303
+ }
304
+
305
+ /**
306
+ * Parser responsible for translation custom twig function: {{ wp_action( ... ) }}
307
+ * See larva/lib/server.js twing.addFunction for details
308
+ *
309
+ * @param string $twig_markup twig markup string.
310
+ * @return string
311
+ */
312
+ function parse_wp_action( string $twig_markup ) : string {
313
+
314
+ $twig_markup = preg_replace_callback(
315
+ '/{{\s*wp_action\s*\(\s*(.*?)\s*\)\s*}}/',
316
+ function ( $matches ) {
317
+
318
+ $args = $matches[1];
319
+
320
+ $args = preg_replace_callback(
321
+ '/\s*([\'"])?(\w+)\1?\s*/',
322
+ function ( $matches ) {
323
+ if ( ! empty( $matches[1] ) ) {
324
+ return sprintf( ' \'%s\'', $matches[2] );
325
+ } else {
326
+ return sprintf( ' $%s', $matches[2] );
327
+ }
328
+ },
329
+ $args
330
+ );
331
+
332
+ return '<?php do_action('. $args . ' ); ?>';
333
+
334
+ },
335
+ $twig_markup
336
+ );
337
+
338
+ return $twig_markup;
339
+ }
340
+
341
+ /**
342
+ * Set debug state.
343
+ *
344
+ * @param boolean $state debug state.
345
+ */
346
+ function set_debug( bool $state ): void {
347
+ global $debug_state;
348
+ $debug_state = $state;
349
+ }
350
+
351
+ /**
352
+ * Log debug values.
353
+ *
354
+ * @param string $msg message to debug.
355
+ */
356
+ function debug_log( string $msg ): void {
357
+ global $debug_state;
358
+ // write out to stderr for now.
359
+ if ( $debug_state ) {
360
+ fwrite( STDERR, sprintf( "%s\n", $msg ) ); // phpcs:ignore
361
+ }
206
362
  }
207
363
 
208
- //EOF
364
+ // Set default to debug mode.
365
+ set_debug( true );