@startupjs/babel-plugin-startupjs 0.61.0 → 0.61.3

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 (2) hide show
  1. package/index.js +136 -37
  2. package/package.json +2 -2
package/index.js CHANGED
@@ -1,59 +1,158 @@
1
1
  const { readFileSync } = require('fs')
2
2
  const { join } = require('path')
3
3
 
4
- const MAGIC_MODULE_NAME = '@startupjs/ui'
4
+ const MAGIC_MODULE_NAME = 'startupjs-ui'
5
5
 
6
- module.exports = function ({ template, types: t }) {
6
+ module.exports = function ({ template, types: t }, { asyncImports = false } = {}) {
7
7
  const buildImport = template('import %%name%% from %%source%%')
8
8
  const buildExport = template('export { default as %%name%% } from %%source%%')
9
+ // when asyncImports is enabled, we replace imports with dynamic imports:
10
+ // import { Button, Card } from 'startupjs-ui'
11
+ // -->
12
+ // const [Button, Card] = await Promise.all([
13
+ // import('startupjs-ui/Button').then(m => m.default),
14
+ // import('startupjs-ui/Card').then(m => m.default)
15
+ // ])
16
+ // Note that this currently does not work in Expo because React Native
17
+ // does not support top-level await yet.
18
+ const buildAsyncImport = source => {
19
+ const importCall = t.callExpression(t.import(), [t.stringLiteral(source)])
20
+ return t.callExpression(
21
+ t.memberExpression(importCall, t.identifier('then')),
22
+ [t.arrowFunctionExpression(
23
+ [t.identifier('m')],
24
+ t.memberExpression(t.identifier('m'), t.identifier('default'))
25
+ )]
26
+ )
27
+ }
28
+ const buildPromiseAll = (names, imports) => t.variableDeclaration('const', [
29
+ t.variableDeclarator(
30
+ t.arrayPattern(names),
31
+ t.awaitExpression(
32
+ t.callExpression(
33
+ t.memberExpression(t.identifier('Promise'), t.identifier('all')),
34
+ [t.arrayExpression(imports)]
35
+ )
36
+ )
37
+ )
38
+ ])
9
39
 
10
40
  return {
11
41
  name: 'Unwrap imports for tree shaking.',
12
42
  visitor: {
13
- ImportDeclaration ($this) {
14
- if ($this.get('source').node.value !== MAGIC_MODULE_NAME) return
15
- let transformed = false // needed to prevent infinite loops
16
- const theImports = $this.get('specifiers')
17
- .map($specifier => {
18
- // pluck out into a separate import
19
- if ($specifier.isImportSpecifier()) {
20
- transformed = true
21
- const originalName = $specifier.get('imported').node.name
43
+ Program ($this) {
44
+ let executed = false
45
+ const asyncImportNames = []
46
+ const asyncImportExpressions = []
47
+ const asyncExportSpecifiers = []
48
+ $this.traverse({
49
+ ImportDeclaration ($path) {
50
+ if ($path.get('source').node.value !== MAGIC_MODULE_NAME) return
51
+ let transformed = false // needed to prevent infinite loops
52
+ const theImports = []
53
+ $path.get('specifiers').forEach($specifier => {
54
+ // pluck out into a separate import
55
+ if ($specifier.isImportSpecifier()) {
56
+ transformed = true
57
+ const originalName = $specifier.get('imported').node.name
58
+ if (!checkNamedExportExists(originalName)) {
59
+ throw $specifier.buildCodeFrameError(
60
+ `Named export "${originalName}" does not exist in "${MAGIC_MODULE_NAME}" "exports" field in package.json`
61
+ )
62
+ }
63
+ const importedName = $specifier.get('local').node.name
64
+ if (asyncImports) {
65
+ asyncImportNames.push(t.identifier(importedName))
66
+ asyncImportExpressions.push(buildAsyncImport(`${MAGIC_MODULE_NAME}/${originalName}`))
67
+ } else {
68
+ theImports.push(buildImport({
69
+ name: importedName,
70
+ source: `${MAGIC_MODULE_NAME}/${originalName}`
71
+ }))
72
+ }
73
+ return
74
+ }
75
+ // pass as is
76
+ theImports.push(t.importDeclaration([$specifier.node], t.stringLiteral(MAGIC_MODULE_NAME)))
77
+ })
78
+ if (!transformed) return
79
+ if (asyncImports) {
80
+ if (theImports.length > 0) {
81
+ $path.replaceWithMultiple(theImports)
82
+ } else {
83
+ $path.remove()
84
+ }
85
+ } else {
86
+ $path.replaceWithMultiple(theImports)
87
+ }
88
+ executed = true
89
+ },
90
+ ExportNamedDeclaration ($path) {
91
+ if ($path.get('source')?.node?.value !== MAGIC_MODULE_NAME) return
92
+ const specifiers = $path.get('specifiers')
93
+ if (asyncImports) {
94
+ specifiers.forEach($specifier => {
95
+ const originalName = $specifier.get('local').node.name
96
+ if (!checkNamedExportExists(originalName)) {
97
+ throw $specifier.buildCodeFrameError(
98
+ `Named export "${originalName}" does not exist in "${MAGIC_MODULE_NAME}" "exports" field in package.json`
99
+ )
100
+ }
101
+ const exportedName = $specifier.get('exported').node.name
102
+ const localId = t.identifier(exportedName)
103
+ asyncImportNames.push(localId)
104
+ asyncImportExpressions.push(buildAsyncImport(`${MAGIC_MODULE_NAME}/${originalName}`))
105
+ asyncExportSpecifiers.push(t.exportSpecifier(localId, t.identifier(exportedName)))
106
+ })
107
+ if (specifiers.length > 0) {
108
+ $path.remove()
109
+ executed = true
110
+ }
111
+ return
112
+ }
113
+ const theExports = specifiers.map($specifier => {
114
+ const originalName = $specifier.get('local').node.name
22
115
  if (!checkNamedExportExists(originalName)) {
23
116
  throw $specifier.buildCodeFrameError(
24
117
  `Named export "${originalName}" does not exist in "${MAGIC_MODULE_NAME}" "exports" field in package.json`
25
118
  )
26
119
  }
27
- const importedName = $specifier.get('local').node.name
28
- return buildImport({
29
- name: importedName,
120
+ const exportedName = $specifier.get('exported').node.name
121
+ return buildExport({
122
+ name: exportedName,
30
123
  source: `${MAGIC_MODULE_NAME}/${originalName}`
31
124
  })
32
- }
33
- // pass as is
34
- return t.importDeclaration([$specifier.node], t.stringLiteral(MAGIC_MODULE_NAME))
35
- })
36
- if (!transformed) return
37
- $this.replaceWithMultiple(theImports)
38
- },
39
- ExportNamedDeclaration ($this) {
40
- if ($this.get('source').node.value !== MAGIC_MODULE_NAME) return
41
- const theExports = $this.get('specifiers')
42
- .map($specifier => {
43
- const originalName = $specifier.get('local').node.name
44
- if (!checkNamedExportExists(originalName)) {
45
- throw $specifier.buildCodeFrameError(
46
- `Named export "${originalName}" does not exist in "${MAGIC_MODULE_NAME}" "exports" field in package.json`
47
- )
48
- }
49
- const exportedName = $specifier.get('exported').node.name
50
- return buildExport({
51
- name: exportedName,
52
- source: `${MAGIC_MODULE_NAME}/${originalName}`
53
125
  })
126
+ $path.replaceWithMultiple(theExports)
127
+ executed = true
128
+ }
129
+ })
130
+ if (asyncImports && asyncImportNames.length > 0) {
131
+ const promiseAll = buildPromiseAll(asyncImportNames, asyncImportExpressions)
132
+ const bodyPaths = $this.get('body')
133
+ let lastImportPath
134
+ bodyPaths.forEach($path => {
135
+ if ($path.isImportDeclaration()) lastImportPath = $path
54
136
  })
55
-
56
- $this.replaceWithMultiple(theExports)
137
+ let promiseAllPath
138
+ if (lastImportPath) {
139
+ promiseAllPath = lastImportPath.insertAfter(promiseAll)[0]
140
+ } else {
141
+ $this.unshiftContainer('body', promiseAll)
142
+ promiseAllPath = $this.get('body.0')
143
+ }
144
+ if (asyncExportSpecifiers.length > 0) {
145
+ const exportDeclaration = t.exportNamedDeclaration(null, asyncExportSpecifiers)
146
+ if (promiseAllPath) {
147
+ promiseAllPath.insertAfter(exportDeclaration)
148
+ } else {
149
+ $this.pushContainer('body', exportDeclaration)
150
+ }
151
+ }
152
+ executed = true
153
+ }
154
+ // re-crawl to update scope bindings
155
+ if (executed) $this.scope.crawl()
57
156
  }
58
157
  }
59
158
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@startupjs/babel-plugin-startupjs",
3
- "version": "0.61.0",
3
+ "version": "0.61.3",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -31,5 +31,5 @@
31
31
  "babel-plugin-tester": "^9.1.0",
32
32
  "jest": "^29.2.1"
33
33
  },
34
- "gitHead": "a891da7cd48b032bdcfd8164c1466e1eef7ed780"
34
+ "gitHead": "4d14704ebbbcddd4de51dc8b1f5432f1f855b544"
35
35
  }