@startupjs/bundler 0.55.11 → 0.56.0-alpha.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/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  ## Requirements
5
5
 
6
6
  ```
7
- react-native-svg: >= 12.1.0
7
+ react-native-svg: >= 14.0.0
8
8
  ```
9
9
 
10
10
  ## Usage
@@ -0,0 +1,44 @@
1
+ // Transform .svg files into React components.
2
+ // This is only used in Metro since on webpack we use @svgr/webpack for the same purpose.
3
+ // ref: https://github.com/kristerkari/react-native-svg-transformer/blob/master/index.js
4
+ const { dirname } = require('path')
5
+ const { resolveConfig, transform } = require('@svgr/core')
6
+
7
+ const defaultSVGRConfig = {
8
+ native: true,
9
+ plugins: ['@svgr/plugin-svgo', '@svgr/plugin-jsx'],
10
+ svgoConfig: {
11
+ plugins: [
12
+ {
13
+ name: 'preset-default',
14
+ params: {
15
+ overrides: {
16
+ inlineStyles: {
17
+ onlyMatchedOnce: false
18
+ },
19
+ removeViewBox: false,
20
+ removeUnknownsAndDefaults: false,
21
+ convertColors: false
22
+ }
23
+ }
24
+ }
25
+ ]
26
+ }
27
+ }
28
+
29
+ module.exports = asyncLoader(async function asyncSvgLoader (source) {
30
+ const filename = this.resourcePath
31
+ const config = {
32
+ ...defaultSVGRConfig,
33
+ ...await resolveConfig(dirname(filename))
34
+ }
35
+ return transform(source, config)
36
+ })
37
+
38
+ // webpack loader must return undefined, so we can't make the actual loader function async
39
+ function asyncLoader (loader) {
40
+ return function (...args) {
41
+ const callback = this.async()
42
+ loader.call(this, ...args).then(result => callback(null, result), callback)
43
+ }
44
+ }
package/lib/callLoader.js CHANGED
@@ -1,4 +1,22 @@
1
1
  // Simple mock to be able to call simple webpack loaders with filename substitution.
2
2
  module.exports = function callLoader (loader, source, filename, options = {}) {
3
- return loader.call({ resourcePath: filename, query: options }, source)
3
+ let isAsync = false
4
+ let resolveAsync
5
+ let rejectAsync
6
+ const markAsync = () => {
7
+ isAsync = true
8
+ return (err, result) => {
9
+ if (err) return rejectAsync(err)
10
+ resolveAsync(result)
11
+ }
12
+ }
13
+ const result = loader.call({ resourcePath: filename, query: options, async: markAsync }, source)
14
+ if (isAsync) {
15
+ return new Promise((resolve, reject) => {
16
+ resolveAsync = resolve
17
+ rejectAsync = reject
18
+ })
19
+ } else {
20
+ return result
21
+ }
4
22
  }
@@ -1,54 +1,83 @@
1
+ // TODO: add support for source maps
1
2
  const babel = require('@babel/core')
3
+ const { isStartupjsPluginEcosystemFile, CONFIG_FILENAME_REGEX } = require('./utils')
2
4
 
3
5
  const PLUGIN_KEYS = ['name', 'for']
6
+ const PROJECT_KEYS = ['plugins', 'modules']
4
7
  const ALL_ENVS = ['client', 'isomorphic', 'server', 'build']
8
+ const MAGIC_IMPORTS = ['startupjs/registry', '@startupjs/registry']
5
9
 
6
10
  module.exports = function eliminatorLoader (source) {
7
- const envs = this.query.envs
8
- if (!envs) throw Error("getEliminatorLoader: envs not provided (for example ['client', 'isomorphic'])")
9
11
  const filename = this.resourcePath
10
12
 
13
+ // ensure that this loader is only used on plugins, startupjs.config.js and loadStartupjsConfig.js files
14
+ if (!(isStartupjsPluginEcosystemFile(filename))) return source
15
+
16
+ const envs = this.query.envs
17
+ if (!envs) throw Error("eliminatorLoader: envs not provided (for example ['client', 'isomorphic'])")
18
+
11
19
  let code = source
12
20
 
13
- // first we need to transform pug to jsx
14
- // so that eliminator doesn't think that there are unused variables
21
+ // STEP 1: convert pug to jsx and auto-load startupjs plugins
15
22
  code = babel.transformSync(code, {
16
23
  filename,
17
24
  babelrc: false,
18
25
  configFile: false,
19
26
  plugins: [
27
+ // support JSX syntax
20
28
  require('@babel/plugin-syntax-jsx'),
29
+ // transform pug to jsx. This generates a bunch of new AST nodes
30
+ // (it's important to do this first before any dead code elimination runs)
21
31
  [require('babel-plugin-transform-react-pug'), {
22
32
  classAttribute: 'styleName'
23
33
  }],
34
+ // support calling sub-components in pug (like <Modal.Header />)
24
35
  [require('@startupjs/babel-plugin-react-pug-classnames'), {
25
36
  classAttribute: 'styleName'
26
37
  }],
38
+ // traverse "exports" of package.json and all dependencies to find all startupjs plugins
39
+ // and automatically import them in the main startupjs.config.js file
27
40
  require('@startupjs/babel-plugin-startupjs-plugins')
28
41
  ]
29
42
  }).code
30
43
 
31
- // then we run the actual eliminator to remove env-specific stuff
44
+ // STEP 2: remove code related to other envs
32
45
  code = babel.transformSync(code, {
33
46
  filename,
34
47
  babelrc: false,
35
48
  configFile: false,
36
49
  plugins: [
50
+ // support JSX syntax
37
51
  require('@babel/plugin-syntax-jsx'),
52
+ // run eliminator to remove code targeting other envs.
53
+ // For example, only keep code related to 'client' and 'isomorphic' envs
54
+ // (in which case any code related to 'server' and 'build' envs will be removed)
38
55
  [require('@startupjs/babel-plugin-eliminator'), {
39
- keepObjectKeysOfFunction: {
40
- createProject: {
41
- magicImports: ['startupjs/registry', 'startupjs/registry.js', '@startupjs/registry', '@startupjs/registry.js'],
42
- targetObjectJsonPath: '$.plugins.*',
43
- ensureOnlyKeys: ['client', 'isomorphic', 'server', 'build'],
44
- keepKeys: envs
45
- },
46
- createPlugin: {
47
- magicImports: ['startupjs/registry', 'startupjs/registry.js', '@startupjs/registry', '@startupjs/registry.js'],
48
- ensureOnlyKeys: [...PLUGIN_KEYS, ...ALL_ENVS],
49
- keepKeys: [...PLUGIN_KEYS, ...envs]
50
- }
51
- }
56
+ trimObjects: [{
57
+ magicFilenameRegex: CONFIG_FILENAME_REGEX,
58
+ magicExport: 'default',
59
+ targetObjectJsonPath: '$.modules.*',
60
+ ensureOnlyKeys: ALL_ENVS,
61
+ keepKeys: envs
62
+ }, {
63
+ magicFilenameRegex: CONFIG_FILENAME_REGEX,
64
+ magicExport: 'default',
65
+ targetObjectJsonPath: '$.plugins.*',
66
+ ensureOnlyKeys: ALL_ENVS,
67
+ keepKeys: envs
68
+ }, {
69
+ magicFilenameRegex: CONFIG_FILENAME_REGEX,
70
+ magicExport: 'default',
71
+ targetObjectJsonPath: '$',
72
+ // envs on the top level are the alias for '$.modules.startupjs'
73
+ ensureOnlyKeys: [...PROJECT_KEYS, ...ALL_ENVS],
74
+ keepKeys: [...PROJECT_KEYS, ...envs]
75
+ }, {
76
+ functionName: 'createPlugin',
77
+ magicImports: MAGIC_IMPORTS,
78
+ ensureOnlyKeys: [...PLUGIN_KEYS, ...ALL_ENVS],
79
+ keepKeys: [...PLUGIN_KEYS, ...envs]
80
+ }]
52
81
  }]
53
82
  ]
54
83
  }).code
@@ -0,0 +1,23 @@
1
+ // Only used for React Native
2
+ const babel = require('@babel/core')
3
+
4
+ module.exports = function startupjsLoader (source) {
5
+ const filename = this.resourcePath
6
+ const platform = this.query.platform
7
+
8
+ // There is a bug in metro when BABEL_ENV is a string "undefined".
9
+ // We have to workaround it and use NODE_ENV.
10
+ const env = (process.env.BABEL_ENV !== 'undefined' && process.env.BABEL_ENV) || process.env.NODE_ENV
11
+
12
+ return babel.transformSync(source, {
13
+ filename,
14
+ babelrc: false,
15
+ configFile: false,
16
+ presets: [
17
+ [require('babel-preset-startupjs/pure'), {
18
+ platform,
19
+ env
20
+ }]
21
+ ]
22
+ }).code
23
+ }
@@ -1,4 +1,5 @@
1
1
  // ref: https://github.com/kristerkari/react-native-stylus-transformer
2
+ // TODO: Refactor `platform` to be just passed externally as an option in metro and in webpack
2
3
  const platformSingleton = require(
3
4
  '@startupjs/babel-plugin-rn-stylename-inline/platformSingleton'
4
5
  )
@@ -7,6 +8,11 @@ const path = require('path')
7
8
  const stylus = require('stylus')
8
9
  const STYLES_PATH = path.join(process.cwd(), 'styles/index.styl')
9
10
 
11
+ let UI_STYLES_PATH
12
+ try {
13
+ UI_STYLES_PATH = require.resolve('@startupjs/ui/styles/index.styl')
14
+ } catch (err) {}
15
+
10
16
  function renderToCSS (src, filename) {
11
17
  let compiled
12
18
  const compiler = stylus(src)
@@ -18,6 +24,10 @@ function renderToCSS (src, filename) {
18
24
  compiler.define(`__${platform.toUpperCase()}__`, true)
19
25
  }
20
26
 
27
+ if (fs.existsSync(UI_STYLES_PATH)) {
28
+ compiler.import(UI_STYLES_PATH)
29
+ }
30
+
21
31
  // TODO: Make this a setting
22
32
  if (fs.existsSync(STYLES_PATH)) {
23
33
  compiler.import(STYLES_PATH)
package/lib/utils.js ADDED
@@ -0,0 +1,12 @@
1
+ // NOTE: startupjs.config.cjs is used by the old bundler. This regex does not include it.
2
+ exports.CONFIG_FILENAME_REGEX = /(?:^|[\\/])startupjs\.config\.m?[jt]sx?$/
3
+ exports.PLUGIN_FILENAME_REGEX = /(?:^|[.\\/])plugin\.[mc]?[jt]sx?$/
4
+ exports.LOAD_CONFIG_FILENAME_REGEX = /(?:^|[\\/])loadStartupjsConfig\.m?[jt]sx?$/
5
+
6
+ exports.isStartupjsPluginEcosystemFile = filename => {
7
+ return (
8
+ exports.PLUGIN_FILENAME_REGEX.test(filename) ||
9
+ exports.CONFIG_FILENAME_REGEX.test(filename) ||
10
+ exports.LOAD_CONFIG_FILENAME_REGEX.test(filename)
11
+ )
12
+ }
package/lib/yamlLoader.js CHANGED
@@ -1,6 +1,6 @@
1
1
  const yaml = require('js-yaml')
2
2
 
3
- module.exports = function mdxExamplesLoader (source) {
3
+ module.exports = function yamlLoader (source) {
4
4
  const yamlData = yaml.load(source)
5
5
  return `export default ${JSON.stringify(yamlData)};`
6
6
  }
@@ -0,0 +1,57 @@
1
+ const platformSingleton = require('@startupjs/babel-plugin-rn-stylename-inline/platformSingleton')
2
+ const stylusToCssLoader = require('./lib/stylusToCssLoader')
3
+ const cssToReactNativeLoader = require('./lib/cssToReactNativeLoader')
4
+ const mdxExamplesLoader = require('./lib/mdxExamplesLoader')
5
+ const getMDXLoader = require('./lib/getMDXLoader')
6
+ const eliminatorLoader = require('./lib/eliminatorLoader')
7
+ const callLoader = require('./lib/callLoader')
8
+ const asyncSvgLoader = require('./lib/asyncSvgLoader')
9
+ const startupjsLoader = require('./lib/startupjsLoader')
10
+
11
+ module.exports.transform = async function startupjsMetroBabelTransform ({
12
+ src, filename, options: { upstreamTransformer, ...options } = {}
13
+ }) {
14
+ upstreamTransformer ??= getUpstreamTransformer()
15
+ const { platform } = options
16
+ platformSingleton.value = platform
17
+
18
+ // from exotic extensions to js
19
+ if (/\.styl$/.test(filename)) {
20
+ // TODO: Refactor `platform` to be just passed externally as an option in metro and in webpack
21
+ src = callLoader(stylusToCssLoader, src, filename)
22
+ src = callLoader(cssToReactNativeLoader, src, filename)
23
+ } else if (/\.css$/.test(filename)) {
24
+ src = callLoader(cssToReactNativeLoader, src, filename)
25
+ } else if (/\.svg$/.test(filename)) {
26
+ src = await callLoader(asyncSvgLoader, src, filename)
27
+ } else if (/\.mdx?$/.test(filename)) {
28
+ const mdxLoader = await getMDXLoader()
29
+ src = callLoader(mdxExamplesLoader, src, filename)
30
+ src = callLoader(mdxLoader, src, filename)
31
+ }
32
+
33
+ // js transformations
34
+ if (/\.[mc]?[jt]sx?$/.test(filename)) {
35
+ src = callLoader(eliminatorLoader, src, filename, { envs: ['client', 'isomorphic'] })
36
+ }
37
+ if ((/\.mdx?$/.test(filename) || /\.[mc]?[jt]sx?$/.test(filename)) && /['"](?:startupjs|@env)['"]/.test(src)) {
38
+ src = callLoader(startupjsLoader, src, filename, { platform })
39
+ }
40
+
41
+ return upstreamTransformer.transform({ src, filename, options })
42
+ }
43
+
44
+ function getUpstreamTransformer () {
45
+ try {
46
+ // Expo
47
+ return require('@expo/metro-config/babel-transformer')
48
+ } catch (err) {
49
+ try {
50
+ // React Native 0.73+
51
+ return require('@react-native/metro-babel-transformer')
52
+ } catch (err) {
53
+ // React Native <0.73
54
+ return require('metro-react-native-babel-transformer')
55
+ }
56
+ }
57
+ }
@@ -0,0 +1,87 @@
1
+ const connect = require('connect')
2
+
3
+ // To pass existing config for modification, pass it as 'upstreamConfig' in options:
4
+ // config = getDefaultConfig(__dirname, { upstreamConfig })
5
+ exports.getDefaultConfig = function getDefaultConfig (projectRoot, { upstreamConfig } = {}) {
6
+ upstreamConfig ??= getUpstreamConfig(projectRoot)
7
+ const isExpo = checkIfExpo(upstreamConfig)
8
+ return {
9
+ ...upstreamConfig,
10
+ transformer: {
11
+ ...upstreamConfig.transformer,
12
+ babelTransformerPath: require.resolve('./metro-babel-transformer.js')
13
+ },
14
+ resolver: {
15
+ ...upstreamConfig.resolver,
16
+ assetExts: upstreamConfig.resolver.assetExts.filter(ext => ext !== 'svg'),
17
+ sourceExts: Array.from(new Set([
18
+ ...(isExpo ? ['expo.ts', 'expo.tsx', 'expo.js', 'expo.jsx', 'expo.mjs', 'expo.cjs'] : []),
19
+ ...(upstreamConfig.resolver.sourceExts || []),
20
+ ...['mjs', 'cjs', 'md', 'mdx', 'css', 'styl', 'svg']
21
+ ]))
22
+ },
23
+ server: {
24
+ ...upstreamConfig.server,
25
+ // TODO: implement a simple parsing of startupjs.config.js
26
+ // to figure out whether we need to load 'server' and 'isomorphic'
27
+ // envs or only the 'build' one.
28
+ // If $.isomorphic.server is turned off then we don't need the enhanceMiddleware at all
29
+ enhanceMiddleware: metroMiddleware => {
30
+ return connect()
31
+ .use(metroMiddleware)
32
+ .use(getStartupjsMiddleware())
33
+ }
34
+ }
35
+ }
36
+ }
37
+
38
+ function getUpstreamConfig (projectRoot) {
39
+ try {
40
+ // Expo
41
+ return require('expo/metro-config').getDefaultConfig(projectRoot, {
42
+ // startupjs has a custom CSS implementation so we don't need to use Expo's
43
+ isCSSEnabled: false
44
+ })
45
+ } catch (err) {
46
+ try {
47
+ // React Native 0.73+
48
+ return require('@react-native/metro-config').getDefaultConfig(projectRoot)
49
+ } catch (err) {
50
+ // React Native <0.73
51
+ return require('metro-config').getDefaultConfig()
52
+ }
53
+ }
54
+ }
55
+
56
+ function checkIfExpo (upstreamConfig) {
57
+ if (/[\\/]@expo[\\/]metro-config[\\/]/.test(upstreamConfig?.transformer?.babelTransformerPath)) {
58
+ return true
59
+ }
60
+ try {
61
+ const path = require.resolve('expo/metro-config')
62
+ return Boolean(path)
63
+ } catch (err) {
64
+ return false
65
+ }
66
+ }
67
+
68
+ function getStartupjsMiddleware () {
69
+ const middlewarePromise = (async () => {
70
+ await import('./nodeRegister.mjs')
71
+ await import('@startupjs/registry/loadStartupjsConfig.auto')
72
+ const { ROOT_MODULE: MODULE } = await import('@startupjs/registry')
73
+ if (!MODULE.options.server) return
74
+ const { createMiddleware } = await import('@startupjs/server')
75
+ return (await createMiddleware()).middleware
76
+ })()
77
+ return function startupjsMiddleware (req, res, next) {
78
+ (async () => {
79
+ try {
80
+ const middleware = await middlewarePromise
81
+ return middleware ? middleware.apply(this, arguments) : next()
82
+ } catch (err) {
83
+ return next(err)
84
+ }
85
+ })()
86
+ }
87
+ }
package/metro.config.js CHANGED
@@ -1,4 +1,29 @@
1
- const defaultAssetExts = require('metro-config/src/defaults/defaults').assetExts
1
+ // DEPRECATED
2
+ console.warn(`
3
+ Using 'startupjs/bundler/metro.config.cjs' directly is deprecated and will be removed soon.
4
+ Instead please change your metro.config.js to:
5
+
6
+ const { getDefaultConfig } = require('startupjs/metro-config')
7
+ const config = getDefaultConfig(__dirname)
8
+ // modify config here if needed
9
+ // ...
10
+ module.exports = config
11
+ `)
12
+
13
+ const DEFAULT_ASSET_EXTS = [
14
+ // Image formats
15
+ 'bmp', 'gif', 'jpg', 'jpeg', 'png', 'psd', 'svg', 'webp',
16
+ // Video formats
17
+ 'm4v', 'mov', 'mp4', 'mpeg', 'mpg', 'webm',
18
+ // Audio formats
19
+ 'aac', 'aiff', 'caf', 'm4a', 'mp3', 'wav',
20
+ // Document formats
21
+ 'html', 'pdf', 'yaml', 'yml',
22
+ // Font formats
23
+ 'otf', 'ttf',
24
+ // Archives (virtual files)
25
+ 'zip'
26
+ ]
2
27
 
3
28
  const EXTENSIONS = ['js', 'jsx', 'json', 'mjs', 'cjs', 'ts', 'tsx', 'md', 'mdx', 'css', 'styl', 'svg']
4
29
 
@@ -13,7 +38,7 @@ module.exports = {
13
38
  babelTransformerPath: require.resolve('./lib/rnTransformer')
14
39
  },
15
40
  resolver: {
16
- assetExts: defaultAssetExts.filter(ext => ext !== 'svg'),
41
+ assetExts: DEFAULT_ASSET_EXTS.filter(ext => ext !== 'svg'),
17
42
  sourceExts: EXTENSIONS
18
43
  }
19
44
  }
package/nodeLoader.mjs CHANGED
@@ -3,6 +3,7 @@ import { fileURLToPath } from 'node:url'
3
3
  import callLoader from './lib/callLoader.js'
4
4
  import yamlLoader from './lib/yamlLoader.js'
5
5
  import eliminatorLoader from './lib/eliminatorLoader.js'
6
+ import { isStartupjsPluginEcosystemFile } from './lib/utils.js'
6
7
 
7
8
  export function resolve (specifier, context, nextResolve) {
8
9
  const { parentURL = null } = context
@@ -33,7 +34,7 @@ export async function load (url, context, nextLoad) {
33
34
  }
34
35
 
35
36
  // process code elimination of other envs for *.plugin.js and startupjs.config.js
36
- if (/(?:[./]plugin\.[mc]?[jt]sx?|startupjs\.config\.js)$/.test(url)) {
37
+ if (isStartupjsPluginEcosystemFile(url)) {
37
38
  const filePath = fileURLToPath(url)
38
39
  let source = await readFile(filePath, 'utf8')
39
40
  source = callLoader(eliminatorLoader, source, filePath, { envs: ['server', 'isomorphic'] })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@startupjs/bundler",
3
- "version": "0.55.11",
3
+ "version": "0.56.0-alpha.1",
4
4
  "description": "Opinionated scripts and configs to develop a react-native-web project",
5
5
  "main": "index.js",
6
6
  "publishConfig": {
@@ -21,23 +21,28 @@
21
21
  "@mdx-js/loader": "^2.3.0",
22
22
  "@mdx-js/mdx": "^2.3.0",
23
23
  "@pmmmwh/react-refresh-webpack-plugin": "^0.5.1",
24
- "@startupjs/babel-plugin-eliminator": "^0.55.0",
24
+ "@startupjs/babel-plugin-eliminator": "^0.56.0-alpha.1",
25
25
  "@startupjs/babel-plugin-react-css-modules": "^6.5.4-1",
26
- "@startupjs/babel-plugin-react-pug-classnames": "^0.55.8",
27
- "@startupjs/babel-plugin-startupjs-plugins": "^0.55.8",
26
+ "@startupjs/babel-plugin-react-pug-classnames": "^0.56.0-alpha.0",
27
+ "@startupjs/babel-plugin-startupjs-plugins": "^0.56.0-alpha.1",
28
28
  "@startupjs/css-to-react-native-transform": "^1.9.0-2",
29
- "@startupjs/plugin": "^0.55.8",
30
- "@svgr/webpack": "~7.0.0",
29
+ "@startupjs/plugin": "^0.56.0-alpha.0",
30
+ "@startupjs/registry": "^0.56.0-alpha.1",
31
+ "@startupjs/server": "^0.56.0-alpha.1",
32
+ "@svgr/core": "^8.1.0",
33
+ "@svgr/plugin-jsx": "^8.1.0",
34
+ "@svgr/plugin-svgo": "^8.1.0",
35
+ "@svgr/webpack": "^8.1.0",
31
36
  "assets-webpack-plugin": "^7.1.1",
32
37
  "autoprefixer": "^10.4.0",
33
38
  "babel-loader": "^8.2.3",
34
39
  "babel-plugin-transform-react-pug": "^7.0.1",
35
- "babel-preset-startupjs": "^0.55.11",
40
+ "babel-preset-startupjs": "^0.56.0-alpha.1",
41
+ "connect": "^3.7.0",
36
42
  "css-loader": "^6.5.0",
37
43
  "css-minimizer-webpack-plugin": "^5.0.0",
38
44
  "file-loader": "^6.2.0",
39
45
  "lodash": "^4.17.20",
40
- "metro-config": "*",
41
46
  "mini-css-extract-plugin": "^2.4.3",
42
47
  "moment": "^2.0.0",
43
48
  "moment-locales-webpack-plugin": "^1.0.7",
@@ -56,5 +61,5 @@
56
61
  "peerDependencies": {
57
62
  "react-native-svg": "*"
58
63
  },
59
- "gitHead": "253c84e79e50a73a194c33fb3d17a22ddb1af58f"
64
+ "gitHead": "30b858bb3203dc9dc064a3b16e060e2cc1777a71"
60
65
  }