7
7
generatePreamble ,
8
8
getSourceMappingUrlForEndOfFile ,
9
9
hasError ,
10
+ isString ,
10
11
rollupToStencilSourceMap ,
11
12
} from '@utils' ;
12
13
import { getCustomElementsBuildConditionals } from '../dist-custom-elements-bundle/custom-elements-build-conditionals' ;
@@ -21,6 +22,17 @@ import { proxyCustomElement } from '../../transformers/component-native/proxy-cu
21
22
import { updateStencilCoreImports } from '../../transformers/update-stencil-core-import' ;
22
23
import ts from 'typescript' ;
23
24
25
+ /**
26
+ * Main output target function for `dist-custom-elements`. This function just
27
+ * does some organizational work to call the other functions in this module,
28
+ * which do actual work of generating the rollup configuration, creating an
29
+ * entry chunk, running, the build, etc.
30
+ *
31
+ * @param config the user-supplied compiler configuration we're using
32
+ * @param compilerCtx the current compiler context
33
+ * @param buildCtx the current build context
34
+ * @returns an empty Promise which won't resolve until the work is done!
35
+ */
24
36
export const outputCustomElements = async (
25
37
config : d . Config ,
26
38
compilerCtx : d . CompilerCtx ,
@@ -30,46 +42,80 @@ export const outputCustomElements = async (
30
42
return ;
31
43
}
32
44
33
- const outputTargets = config . outputTargets . filter ( isOutputTargetDistCustomElements ) ;
45
+ const outputTargets = ( config . outputTargets ?? [ ] ) . filter ( isOutputTargetDistCustomElements ) ;
34
46
if ( outputTargets . length === 0 ) {
35
47
return ;
36
48
}
37
49
38
50
const bundlingEventMessage = 'generate custom elements' ;
39
51
const timespan = buildCtx . createTimeSpan ( `${ bundlingEventMessage } started` ) ;
40
52
41
- await Promise . all ( outputTargets . map ( ( o ) => bundleCustomElements ( config , compilerCtx , buildCtx , o ) ) ) ;
53
+ await Promise . all ( outputTargets . map ( ( target ) => bundleCustomElements ( config , compilerCtx , buildCtx , target ) ) ) ;
42
54
43
55
timespan . finish ( `${ bundlingEventMessage } finished` ) ;
44
56
} ;
45
57
46
- const bundleCustomElements = async (
58
+ /**
59
+ * Get bundle options for our current build and compiler context which we'll use
60
+ * to generate a Rollup build and so on.
61
+ *
62
+ * @param config user-supplied Stencil configuration
63
+ * @param buildCtx the current build context
64
+ * @param compilerCtx the current compiler context
65
+ * @param outputTarget the outputTarget we're currently dealing with
66
+ * @returns bundle options suitable for generating a rollup configuration
67
+ */
68
+ export const getBundleOptions = (
69
+ config : d . Config ,
70
+ buildCtx : d . BuildCtx ,
71
+ compilerCtx : d . CompilerCtx ,
72
+ outputTarget : d . OutputTargetDistCustomElements
73
+ ) : BundleOptions => ( {
74
+ id : 'customElements' ,
75
+ platform : 'client' ,
76
+ conditionals : getCustomElementsBuildConditionals ( config , buildCtx . components ) ,
77
+ customTransformers : getCustomElementCustomTransformer ( config , compilerCtx , buildCtx . components , outputTarget ) ,
78
+ externalRuntime : ! ! outputTarget . externalRuntime ,
79
+ inlineWorkers : true ,
80
+ inputs : {
81
+ // Here we prefix our index chunk with '\0' to tell Rollup that we're
82
+ // going to be using virtual modules with this module. A leading '\0'
83
+ // prevents other plugins from messing with the module. We generate a
84
+ // string for the index chunk below in the `loader` property.
85
+ //
86
+ // @see {@link https://rollupjs.org/guide/en/#conventions } for more info.
87
+ index : '\0core' ,
88
+ } ,
89
+ loader : {
90
+ '\0core' : generateEntryPoint ( outputTarget ) ,
91
+ } ,
92
+ inlineDynamicImports : outputTarget . inlineDynamicImports ,
93
+ preserveEntrySignatures : 'allow-extension' ,
94
+ } ) ;
95
+
96
+ /**
97
+ * Get bundle options for rollup, run the rollup build, optionally minify the
98
+ * output, and write files to disk.
99
+ * @param config user-supplied Stencil configuration
100
+ * @param buildCtx the current build context
101
+ * @param compilerCtx the current compiler context
102
+ * @param outputTarget the outputTarget we're currently dealing with
103
+ * @returns an empty promise
104
+ */
105
+
106
+ export const bundleCustomElements = async (
47
107
config : d . Config ,
48
108
compilerCtx : d . CompilerCtx ,
49
109
buildCtx : d . BuildCtx ,
50
110
outputTarget : d . OutputTargetDistCustomElements
51
111
) => {
52
112
try {
53
- const bundleOpts : BundleOptions = {
54
- id : 'customElements' ,
55
- platform : 'client' ,
56
- conditionals : getCustomElementsBuildConditionals ( config , buildCtx . components ) ,
57
- customTransformers : getCustomElementCustomTransformer ( config , compilerCtx , buildCtx . components , outputTarget ) ,
58
- externalRuntime : ! ! outputTarget . externalRuntime ,
59
- inlineWorkers : true ,
60
- inputs : {
61
- index : '\0core' ,
62
- } ,
63
- loader : {
64
- '\0core' : generateEntryPoint ( outputTarget ) ,
65
- } ,
66
- inlineDynamicImports : outputTarget . inlineDynamicImports ,
67
- preserveEntrySignatures : 'allow-extension' ,
68
- } ;
113
+ const bundleOpts = getBundleOptions ( config , buildCtx , compilerCtx , outputTarget ) ;
69
114
70
115
addCustomElementInputs ( buildCtx , bundleOpts ) ;
71
116
72
117
const build = await bundleOutput ( config , compilerCtx , buildCtx , bundleOpts ) ;
118
+
73
119
if ( build ) {
74
120
const rollupOutput = await build . generate ( {
75
121
banner : generatePreamble ( config ) ,
@@ -81,6 +127,20 @@ const bundleCustomElements = async (
81
127
preferConst : true ,
82
128
} ) ;
83
129
130
+ // the output target should have been validated at this point - as a result, we expect this field
131
+ // to have been backfilled if it wasn't provided
132
+ const outputTargetDir : string = outputTarget . dir ! ;
133
+
134
+ // besides, if it isn't here we do a diagnostic and an early return
135
+ if ( ! isString ( outputTargetDir ) ) {
136
+ buildCtx . diagnostics . push ( {
137
+ level : 'error' ,
138
+ type : 'build' ,
139
+ messageText : 'dist-custom-elements output target provided with no output target directory!' ,
140
+ } ) ;
141
+ return ;
142
+ }
143
+
84
144
const minify = outputTarget . externalRuntime || outputTarget . minify !== true ? false : config . minifyJs ;
85
145
const files = rollupOutput . output . map ( async ( bundle ) => {
86
146
if ( bundle . type === 'chunk' ) {
@@ -96,19 +156,15 @@ const bundleCustomElements = async (
96
156
buildCtx . diagnostics . push ( ...optimizeResults . diagnostics ) ;
97
157
if ( ! hasError ( optimizeResults . diagnostics ) && typeof optimizeResults . output === 'string' ) {
98
158
code = optimizeResults . output ;
99
- sourceMap = optimizeResults . sourceMap ;
100
159
}
101
- if ( sourceMap ) {
160
+ if ( optimizeResults . sourceMap ) {
161
+ sourceMap = optimizeResults . sourceMap ;
102
162
code = code + getSourceMappingUrlForEndOfFile ( bundle . fileName ) ;
103
- await compilerCtx . fs . writeFile (
104
- join ( outputTarget . dir , bundle . fileName + '.map' ) ,
105
- JSON . stringify ( sourceMap ) ,
106
- {
107
- outputTargetType : outputTarget . type ,
108
- }
109
- ) ;
163
+ await compilerCtx . fs . writeFile ( join ( outputTargetDir , bundle . fileName + '.map' ) , JSON . stringify ( sourceMap ) , {
164
+ outputTargetType : outputTarget . type ,
165
+ } ) ;
110
166
}
111
- await compilerCtx . fs . writeFile ( join ( outputTarget . dir , bundle . fileName ) , code , {
167
+ await compilerCtx . fs . writeFile ( join ( outputTargetDir , bundle . fileName ) , code , {
112
168
outputTargetType : outputTarget . type ,
113
169
} ) ;
114
170
}
@@ -125,8 +181,11 @@ const bundleCustomElements = async (
125
181
* @param buildCtx the context for the current build
126
182
* @param bundleOpts the bundle options to store the virtual modules under. acts as an output parameter
127
183
*/
128
- const addCustomElementInputs = ( buildCtx : d . BuildCtx , bundleOpts : BundleOptions ) : void => {
184
+ export const addCustomElementInputs = ( buildCtx : d . BuildCtx , bundleOpts : BundleOptions ) : void => {
129
185
const components = buildCtx . components ;
186
+ // an array to store the imports of these modules that we're going to add to our entry chunk
187
+ const indexImports : string [ ] = [ ] ;
188
+
130
189
components . forEach ( ( cmp ) => {
131
190
const exp : string [ ] = [ ] ;
132
191
const exportName = dashToPascalCase ( cmp . tagName ) ;
@@ -136,26 +195,39 @@ const addCustomElementInputs = (buildCtx: d.BuildCtx, bundleOpts: BundleOptions)
136
195
137
196
if ( cmp . isPlain ) {
138
197
exp . push ( `export { ${ importName } as ${ exportName } } from '${ cmp . sourceFilePath } ';` ) ;
198
+ indexImports . push ( `export { {${ exportName } } from '${ coreKey } ';` ) ;
139
199
} else {
140
200
// the `importName` may collide with the `exportName`, alias it just in case it does with `importAs`
141
201
exp . push (
142
202
`import { ${ importName } as ${ importAs } , defineCustomElement as cmpDefCustomEle } from '${ cmp . sourceFilePath } ';`
143
203
) ;
144
204
exp . push ( `export const ${ exportName } = ${ importAs } ;` ) ;
145
205
exp . push ( `export const defineCustomElement = cmpDefCustomEle;` ) ;
206
+
207
+ // Here we push an export (with a rename for `defineCustomElement` for
208
+ // this component onto our array which references the `coreKey` (prefixed
209
+ // with `\0`). We have to do this so that our import is referencing the
210
+ // correct virtual module, if we instead referenced, for instance,
211
+ // `cmp.sourceFilePath`, we would end up with duplicated modules in our
212
+ // output.
213
+ indexImports . push (
214
+ `export { ${ exportName } , defineCustomElement as defineCustomElement${ exportName } } from '${ coreKey } ';`
215
+ ) ;
146
216
}
147
217
148
218
bundleOpts . inputs [ cmp . tagName ] = coreKey ;
149
- bundleOpts . loader [ coreKey ] = exp . join ( '\n' ) ;
219
+ bundleOpts . loader ! [ coreKey ] = exp . join ( '\n' ) ;
150
220
} ) ;
221
+
222
+ bundleOpts . loader ! [ '\0core' ] += indexImports . join ( '\n' ) ;
151
223
} ;
152
224
153
225
/**
154
226
* Generate the entrypoint (`index.ts` file) contents for the `dist-custom-elements` output target
155
227
* @param outputTarget the output target's configuration
156
228
* @returns the stringified contents to be placed in the entrypoint
157
229
*/
158
- const generateEntryPoint = ( outputTarget : d . OutputTargetDistCustomElements ) : string => {
230
+ export const generateEntryPoint = ( outputTarget : d . OutputTargetDistCustomElements ) : string => {
159
231
const imp : string [ ] = [ ] ;
160
232
161
233
imp . push (
@@ -173,6 +245,7 @@ const generateEntryPoint = (outputTarget: d.OutputTargetDistCustomElements): str
173
245
/**
174
246
* Get the series of custom transformers that will be applied to a Stencil project's source code during the TypeScript
175
247
* transpilation process
248
+ *
176
249
* @param config the configuration for the Stencil project
177
250
* @param compilerCtx the current compiler context
178
251
* @param components the components that will be compiled as a part of the current build
0 commit comments