@plone/babel-preset-razzle 1.0.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Jared Palmer
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,28 @@
1
+ > [!IMPORTANT]
2
+ > This package is a maintained fork of the original [`babel-preset-razzle`](https://github.com/jaredpalmer/razzle/tree/master/packages/babel-preset-razzle).
3
+ > The upstream project is currently unmaintained, so we forked it into the Volto monorepo to keep its dependencies updated and address security issues.
4
+ > All upstream attributions are preserved below.
5
+
6
+ # @plone/babel-preset-razzle
7
+
8
+ This package includes the [Babel](https://babeljs.io) preset used by [Razzle](https://github.com/jaredpalmer/razzle).
9
+
10
+ ## Usage in Razzle projects
11
+
12
+ The easiest way to use this configuration is with Razzle, which includes it by default.
13
+
14
+ ## Usage outside of Razzle
15
+
16
+ If you want to use this Babel preset in a project not built with Razzle, you can install it through the following steps.
17
+
18
+ First, [install Babel](https://babeljs.io/setup/).
19
+
20
+ Then create a file named `.babelrc` with the following contents in the root folder of your project:
21
+
22
+ ```js
23
+ {
24
+ "presets": ["@plone/razzle"]
25
+ }
26
+ ```
27
+
28
+ This preset uses the `useBuiltIns` option with [`transform-object-rest-spread`](https://babeljs.io/docs/babel-plugin-transform-object-rest-spread/), which assumes that `Object.assign` is available or polyfilled.
@@ -0,0 +1,27 @@
1
+ const commonjsPlugin = require('@babel/plugin-transform-modules-commonjs');
2
+
3
+ module.exports = function (api, options, dirname) {
4
+ const commonjs = commonjsPlugin.default(api, options, dirname);
5
+ return {
6
+ visitor: {
7
+ Program: {
8
+ exit: function (path, state) {
9
+ let foundModuleExports = false;
10
+ path.traverse({
11
+ MemberExpression: function (expressionPath) {
12
+ if (expressionPath.node.object.name !== 'module') return;
13
+ if (expressionPath.node.property.name !== 'exports') return;
14
+ foundModuleExports = true;
15
+ },
16
+ });
17
+
18
+ if (!foundModuleExports) {
19
+ return;
20
+ }
21
+
22
+ commonjs.visitor.Program.exit.call(this, path, state);
23
+ },
24
+ },
25
+ },
26
+ };
27
+ };
@@ -0,0 +1,97 @@
1
+ 'use strict';
2
+
3
+ module.exports = function (opts) {
4
+ const t = opts.types;
5
+ return {
6
+ inherits: require('babel-plugin-syntax-jsx'),
7
+ visitor: {
8
+ JSXElement: (_path, state) => {
9
+ state.set('jsx', true);
10
+ },
11
+
12
+ // Fragment syntax is still JSX since it compiles to createElement(),
13
+ // but JSXFragment is not a JSXElement
14
+ JSXFragment: (_path, state) => {
15
+ state.set('jsx', true);
16
+ },
17
+
18
+ Program: {
19
+ exit: (path, state) => {
20
+ if (state.get('jsx')) {
21
+ const pragma = t.identifier(state.opts.pragma);
22
+ let importAs = pragma;
23
+
24
+ // if there's already a React in scope, use that instead of adding an import
25
+ const existingBinding =
26
+ state.opts.reuseImport !== false &&
27
+ state.opts.importAs &&
28
+ path.scope.getBinding(state.opts.importAs);
29
+
30
+ // var _jsx = _pragma.createElement;
31
+ if (state.opts.property) {
32
+ if (state.opts.importAs) {
33
+ importAs = t.identifier(state.opts.importAs);
34
+ } else {
35
+ importAs = path.scope.generateUidIdentifier('pragma');
36
+ }
37
+
38
+ const mapping = t.variableDeclaration('var', [
39
+ t.variableDeclarator(
40
+ pragma,
41
+ t.memberExpression(
42
+ importAs,
43
+ t.identifier(state.opts.property),
44
+ ),
45
+ ),
46
+ ]);
47
+
48
+ // if the React binding came from a require('react'),
49
+ // make sure that our usage comes after it.
50
+ let newPath;
51
+ if (
52
+ existingBinding &&
53
+ t.isVariableDeclarator(existingBinding.path.node) &&
54
+ t.isCallExpression(existingBinding.path.node.init) &&
55
+ t.isIdentifier(existingBinding.path.node.init.callee) &&
56
+ existingBinding.path.node.init.callee.name === 'require'
57
+ ) {
58
+ [newPath] =
59
+ existingBinding.path.parentPath.insertAfter(mapping);
60
+ } else {
61
+ // @ts-ignore
62
+ [newPath] = path.unshiftContainer('body', mapping);
63
+ }
64
+
65
+ for (const declar of newPath.get('declarations')) {
66
+ path.scope.registerBinding(newPath.node.kind, declar);
67
+ }
68
+ }
69
+
70
+ if (!existingBinding) {
71
+ const importSpecifier = t.importDeclaration(
72
+ [
73
+ state.opts.import
74
+ ? // import { $import as _pragma } from '$module'
75
+ t.importSpecifier(
76
+ importAs,
77
+ t.identifier(state.opts.import),
78
+ )
79
+ : state.opts.importNamespace
80
+ ? t.importNamespaceSpecifier(importAs)
81
+ : // import _pragma from '$module'
82
+ t.importDefaultSpecifier(importAs),
83
+ ],
84
+ t.stringLiteral(state.opts.module || 'react'),
85
+ );
86
+
87
+ const [newPath] = path.unshiftContainer('body', importSpecifier);
88
+ for (const specifier of newPath.get('specifiers')) {
89
+ path.scope.registerBinding('module', specifier);
90
+ }
91
+ }
92
+ }
93
+ },
94
+ },
95
+ },
96
+ };
97
+ };
@@ -0,0 +1,77 @@
1
+ const chalk = require('chalk');
2
+
3
+ module.exports = function (opts) {
4
+ const t = opts.types;
5
+ let onWarning = null;
6
+ opts.caller((caller) => {
7
+ onWarning = caller.onWarning;
8
+ return ''; // Intentionally empty to not invalidate cache
9
+ });
10
+
11
+ if (typeof onWarning !== 'function') {
12
+ return { visitor: {} };
13
+ }
14
+
15
+ const warn = onWarning;
16
+ return {
17
+ visitor: {
18
+ ExportDefaultDeclaration: function (path) {
19
+ const def = path.node.declaration;
20
+
21
+ if (
22
+ !(
23
+ def.type === 'ArrowFunctionExpression' ||
24
+ def.type === 'FunctionDeclaration'
25
+ )
26
+ ) {
27
+ return;
28
+ }
29
+
30
+ switch (def.type) {
31
+ case 'ArrowFunctionExpression': {
32
+ warn(
33
+ [
34
+ chalk.yellow.bold(
35
+ 'Anonymous arrow functions cause Fast Refresh to not preserve local component state.',
36
+ ),
37
+ 'Please add a name to your function, for example:',
38
+ '',
39
+ chalk.bold('Before'),
40
+ chalk.cyan('export default () => <div />;'),
41
+ '',
42
+ chalk.bold('After'),
43
+ chalk.cyan('const Named = () => <div />;'),
44
+ chalk.cyan('export default Named;'),
45
+ ].join('\n'),
46
+ );
47
+ break;
48
+ }
49
+ case 'FunctionDeclaration': {
50
+ const isAnonymous = !Boolean(def.id);
51
+ if (isAnonymous) {
52
+ warn(
53
+ [
54
+ chalk.yellow.bold(
55
+ 'Anonymous function declarations cause Fast Refresh to not preserve local component state.',
56
+ ),
57
+ 'Please add a name to your function, for example:',
58
+ '',
59
+ chalk.bold('Before'),
60
+ chalk.cyan('export default function () { /* ... */ }'),
61
+ '',
62
+ chalk.bold('After'),
63
+ chalk.cyan('export default function Named() { /* ... */ }'),
64
+ ].join('\n'),
65
+ );
66
+ }
67
+ break;
68
+ }
69
+ default: {
70
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
71
+ const never = def;
72
+ }
73
+ }
74
+ },
75
+ },
76
+ };
77
+ };
@@ -0,0 +1,68 @@
1
+ 'use strict';
2
+
3
+ // matches any hook-like (the default)
4
+ const isHook = /^use[A-Z]/;
5
+
6
+ // matches only built-in hooks provided by React et al
7
+ const isBuiltInHook =
8
+ /^use(Callback|Context|DebugValue|Effect|ImperativeHandle|LayoutEffect|Memo|Reducer|Ref|State)$/;
9
+
10
+ module.exports = function (opts) {
11
+ const t = opts.types;
12
+ const visitor = {
13
+ CallExpression: function (path, state) {
14
+ const onlyBuiltIns = state.opts.onlyBuiltIns;
15
+
16
+ // if specified, options.lib is a list of libraries that provide hook functions
17
+ const libs =
18
+ state.opts.lib &&
19
+ (state.opts.lib === true
20
+ ? ['react', 'preact/hooks']
21
+ : [].concat(state.opts.lib));
22
+
23
+ // skip function calls that are not the init of a variable declaration:
24
+ if (!t.isVariableDeclarator(path.parent)) return;
25
+
26
+ // skip function calls where the return value is not Array-destructured:
27
+ if (!t.isArrayPattern(path.parent.id)) return;
28
+
29
+ // name of the (hook) function being called:
30
+ const hookName = path.node.callee.name;
31
+
32
+ if (libs) {
33
+ const binding = path.scope.getBinding(hookName);
34
+ // not an import
35
+ if (!binding || binding.kind !== 'module') return;
36
+
37
+ const specifier = binding.path.parent.source.value;
38
+ // not a match
39
+ if (!libs.some((lib) => lib === specifier)) return;
40
+ }
41
+
42
+ // only match function calls with names that look like a hook
43
+ if (!(onlyBuiltIns ? isBuiltInHook : isHook).test(hookName)) return;
44
+
45
+ path.parent.id = t.objectPattern(
46
+ path.parent.id.elements.reduce((patterns, element, i) => {
47
+ if (element === null) {
48
+ return patterns;
49
+ }
50
+
51
+ return patterns.concat(
52
+ t.objectProperty(t.numericLiteral(i), element),
53
+ );
54
+ }, []),
55
+ );
56
+ },
57
+ };
58
+
59
+ return {
60
+ name: 'optimize-hook-destructuring',
61
+ visitor: {
62
+ // this is a workaround to run before preset-env destroys destructured assignments
63
+ Program: function (path, state) {
64
+ path.traverse(visitor, state);
65
+ },
66
+ },
67
+ };
68
+ };
package/index.js ADDED
@@ -0,0 +1,138 @@
1
+ 'use strict';
2
+
3
+ const path = require('path');
4
+ const PluginItem = require('@babel/core').PluginItem;
5
+ const env = process.env.NODE_ENV;
6
+ const isProduction = env === 'production';
7
+ const isDevelopment = env === 'development';
8
+ const isTest = env === 'test';
9
+
10
+ // Taken from https://github.com/babel/babel/commit/d60c5e1736543a6eac4b549553e107a9ba967051#diff-b4beead8ad9195361b4537601cc22532R158
11
+ const supportsStaticESM = function (caller) {
12
+ return !!caller && caller.supportsStaticESM;
13
+ };
14
+
15
+ module.exports = function (api, options) {
16
+ options = options || {};
17
+
18
+ const supportsESM = api.caller(supportsStaticESM);
19
+ const isServer = api.caller(function (caller) {
20
+ return !!caller && caller.isServer;
21
+ });
22
+ const isModern = api.caller(function (caller) {
23
+ return !!caller && caller.isModern;
24
+ });
25
+
26
+ const isLaxModern =
27
+ isModern ||
28
+ ((options['preset-env'] || {}).targets &&
29
+ options['preset-env'].targets.esmodules === true);
30
+
31
+ const presetEnvConfig = Object.assign(
32
+ {
33
+ // In the test environment `modules` is often needed to be set to true, babel figures that out by itself using the `'auto'` option
34
+ // In production/development this option is set to `false` so that webpack can handle import/export with tree-shaking
35
+ modules: 'auto',
36
+ exclude: ['transform-typeof-symbol'],
37
+ },
38
+ options['preset-env'] || {},
39
+ );
40
+
41
+ // When transpiling for the server or tests, target the current Node version
42
+ // if not explicitly specified:
43
+ if (
44
+ (isServer || isTest) &&
45
+ (!presetEnvConfig.targets ||
46
+ !(
47
+ typeof presetEnvConfig.targets === 'object' &&
48
+ 'node' in presetEnvConfig.targets
49
+ ))
50
+ ) {
51
+ presetEnvConfig.targets = {
52
+ // Targets the current process' version of Node. This requires apps be
53
+ // built and deployed on the same version of Node.
54
+ node: 'current',
55
+ };
56
+ }
57
+
58
+ // specify a preset to use instead of @babel/preset-env
59
+ const customModernPreset =
60
+ isLaxModern && options['experimental-modern-preset'];
61
+
62
+ const result = {
63
+ sourceType: 'unambiguous',
64
+ presets: [
65
+ customModernPreset || [
66
+ require('@babel/preset-env').default,
67
+ presetEnvConfig,
68
+ ],
69
+ [
70
+ require('@babel/preset-react'),
71
+ Object.assign(
72
+ {
73
+ // This adds @babel/plugin-transform-react-jsx-source and
74
+ // @babel/plugin-transform-react-jsx-self automatically in development
75
+ development: isDevelopment || isTest,
76
+ },
77
+ (options['preset-react'] || {}).runtime !== 'automatic'
78
+ ? { pragma: '__jsx' }
79
+ : {},
80
+ options['preset-react'] || {},
81
+ ),
82
+ ],
83
+ options['preset-typescript'] !== false && [
84
+ require('@babel/preset-typescript'),
85
+ Object.assign(
86
+ { allowNamespaces: true, allExtensions: true, isTSX: true },
87
+ options['preset-typescript'] || {},
88
+ ),
89
+ ],
90
+ ].filter(Boolean),
91
+ plugins: [
92
+ (options['preset-react'] || {}).runtime !== 'automatic' && [
93
+ require('./babel-plugins/jsx-pragma'),
94
+ {
95
+ // This produces the following injected import for modules containing JSX:
96
+ // import React from 'react';
97
+ // var __jsx = React.createElement;
98
+ module: 'react',
99
+ importAs: 'React',
100
+ pragma: '__jsx',
101
+ property: 'createElement',
102
+ },
103
+ ],
104
+ [
105
+ require('./babel-plugins/optimize-hook-destructuring'),
106
+ {
107
+ // only optimize hook functions imported from React/Preact
108
+ lib: true,
109
+ },
110
+ ],
111
+ !isServer && [
112
+ require('@babel/plugin-transform-runtime'),
113
+ Object.assign(
114
+ {
115
+ corejs: false,
116
+ helpers: true,
117
+ regenerator: true,
118
+ useESModules: supportsESM && presetEnvConfig.modules !== 'commonjs',
119
+ absoluteRuntime: path.dirname(
120
+ require.resolve('@babel/runtime/package.json'),
121
+ ),
122
+ version: require('@babel/runtime/package.json').version,
123
+ },
124
+ options['transform-runtime'] || {},
125
+ ),
126
+ ],
127
+ isProduction && [
128
+ require('babel-plugin-transform-react-remove-prop-types'),
129
+ {
130
+ removeImport: true,
131
+ },
132
+ ],
133
+ isServer && require('@babel/plugin-syntax-bigint'),
134
+ ].filter(Boolean),
135
+ };
136
+
137
+ return result;
138
+ };
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@plone/babel-preset-razzle",
3
+ "version": "1.0.1",
4
+ "description": "Babel presets for Razzle",
5
+ "main": "index.js",
6
+ "author": "jaredpalmer",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com:jaredpalmer/razzle.git",
10
+ "directory": "packages/babel-preset-razzle"
11
+ },
12
+ "license": "MIT",
13
+ "files": [
14
+ "index.js",
15
+ "babel-plugins/amp-attributes.js",
16
+ "babel-plugins/commonjs.js",
17
+ "babel-plugins/jsx-pragma.js",
18
+ "babel-plugins/no-anonymous-default-export.js",
19
+ "babel-plugins/optimize-hook-destructuring.js"
20
+ ],
21
+ "publishConfig": {
22
+ "access": "public"
23
+ },
24
+ "dependencies": {
25
+ "@babel/core": "^7.28.5",
26
+ "@babel/plugin-syntax-bigint": "^7.8.3",
27
+ "@babel/plugin-transform-modules-commonjs": "^7.10.4",
28
+ "@babel/plugin-transform-runtime": "^7.9.0",
29
+ "@babel/preset-env": "^7.28.5",
30
+ "@babel/preset-react": "^7.28.5",
31
+ "@babel/preset-typescript": "^7.28.5",
32
+ "@babel/runtime": "^7.28.4",
33
+ "babel-plugin-syntax-jsx": "^6.18.0",
34
+ "babel-plugin-transform-react-remove-prop-types": "^0.4.24",
35
+ "chalk": "^5.6.2"
36
+ },
37
+ "devDependencies": {
38
+ "release-it": "^20.0.1"
39
+ },
40
+ "scripts": {
41
+ "dry-release": "release-it --dry-run",
42
+ "release": "release-it",
43
+ "release-major-alpha": "release-it major --preRelease=alpha",
44
+ "release-alpha": "release-it --preRelease=alpha"
45
+ }
46
+ }