@@ -4,6 +4,8 @@ import isReference from '../utils/isReference.js';
4
4
import flattenReference from '../utils/flattenReference.js' ;
5
5
import globalWhitelist from '../utils/globalWhitelist.js' ;
6
6
import reservedNames from '../utils/reservedNames.js' ;
7
+ import namespaces from '../utils/namespaces.js' ;
8
+ import { removeNode , removeObjectKey } from '../utils/removeNode.js' ;
7
9
import getIntro from './shared/utils/getIntro.js' ;
8
10
import getOutro from './shared/utils/getOutro.js' ;
9
11
import processCss from './shared/processCss.js' ;
@@ -21,6 +23,7 @@ export default class Generator {
21
23
this . helpers = new Set ( ) ;
22
24
this . components = new Set ( ) ;
23
25
this . events = new Set ( ) ;
26
+ this . importedComponents = new Map ( ) ;
24
27
25
28
this . bindingGroups = [ ] ;
26
29
@@ -257,67 +260,42 @@ export default class Generator {
257
260
} ;
258
261
}
259
262
260
- parseJs ( ) {
263
+ parseJs ( ssr ) {
261
264
const { source } = this ;
262
265
const { js } = this . parsed ;
263
266
264
267
const imports = this . imports ;
265
268
const computations = [ ] ;
266
- let defaultExport = null ;
267
269
const templateProperties = { } ;
268
270
271
+ let namespace = null ;
272
+ let hasJs = ! ! js ;
273
+
269
274
if ( js ) {
270
275
this . addSourcemapLocations ( js . content ) ;
276
+ const body = js . content . body . slice ( ) ; // slice, because we're going to be mutating the original
271
277
272
278
// imports need to be hoisted out of the IIFE
273
- for ( let i = 0 ; i < js . content . body . length ; i += 1 ) {
274
- const node = js . content . body [ i ] ;
279
+ for ( let i = 0 ; i < body . length ; i += 1 ) {
280
+ const node = body [ i ] ;
275
281
if ( node . type === 'ImportDeclaration' ) {
276
- let a = node . start ;
277
- let b = node . end ;
278
- while ( / [ \t ] / . test ( source [ a - 1 ] ) ) a -= 1 ;
279
- while ( source [ b ] === '\n' ) b += 1 ;
280
-
282
+ removeNode ( this . code , js . content , node ) ;
281
283
imports . push ( node ) ;
282
- this . code . remove ( a , b ) ;
284
+
283
285
node . specifiers . forEach ( specifier => {
284
286
this . importedNames . add ( specifier . local . name ) ;
285
287
} ) ;
286
288
}
287
289
}
288
290
289
- defaultExport = js . content . body . find ( node => node . type === 'ExportDefaultDeclaration' ) ;
291
+ const defaultExport = body . find ( node => node . type === 'ExportDefaultDeclaration' ) ;
290
292
291
293
if ( defaultExport ) {
292
- const finalNode = js . content . body [ js . content . body . length - 1 ] ;
293
- if ( defaultExport === finalNode ) {
294
- // export is last property, we can just return it
295
- this . code . overwrite ( defaultExport . start , defaultExport . declaration . start , `return ` ) ;
296
- } else {
297
- const { declarations } = annotateWithScopes ( js ) ;
298
- let template = 'template' ;
299
- for ( let i = 1 ; declarations . has ( template ) ; template = `template_${ i ++ } ` ) ;
300
-
301
- this . code . overwrite ( defaultExport . start , defaultExport . declaration . start , `var ${ template } = ` ) ;
302
-
303
- let i = defaultExport . start ;
304
- while ( / \s / . test ( source [ i - 1 ] ) ) i -- ;
305
-
306
- const indentation = source . slice ( i , defaultExport . start ) ;
307
- this . code . appendLeft ( finalNode . end , `\n\n${ indentation } return ${ template } ;` ) ;
308
- }
309
-
310
294
defaultExport . declaration . properties . forEach ( prop => {
311
295
templateProperties [ prop . key . name ] = prop ;
312
296
} ) ;
313
-
314
- this . code . prependRight ( js . content . start , `var ${ this . alias ( 'template' ) } = (function () {` ) ;
315
- } else {
316
- this . code . prependRight ( js . content . start , '(function () {' ) ;
317
297
}
318
298
319
- this . code . appendLeft ( js . content . end , '}());' ) ;
320
-
321
299
[ 'helpers' , 'events' , 'components' ] . forEach ( key => {
322
300
if ( templateProperties [ key ] ) {
323
301
templateProperties [ key ] . value . properties . forEach ( prop => {
@@ -353,11 +331,102 @@ export default class Generator {
353
331
354
332
templateProperties . computed . value . properties . forEach ( prop => visit ( prop . key . name ) ) ;
355
333
}
334
+
335
+ if ( templateProperties . namespace ) {
336
+ const ns = templateProperties . namespace . value . value ;
337
+ namespace = namespaces [ ns ] || ns ;
338
+
339
+ removeObjectKey ( this . code , defaultExport . declaration , 'namespace' ) ;
340
+ }
341
+
342
+ if ( templateProperties . components ) {
343
+ let hasNonImportedComponent = false ;
344
+ templateProperties . components . value . properties . forEach ( property => {
345
+ const key = property . key . name ;
346
+ const value = source . slice ( property . value . start , property . value . end ) ;
347
+ if ( this . importedNames . has ( value ) ) {
348
+ this . importedComponents . set ( key , value ) ;
349
+ } else {
350
+ hasNonImportedComponent = true ;
351
+ }
352
+ } ) ;
353
+ if ( hasNonImportedComponent ) {
354
+ // remove the specific components that were imported, as we'll refer to them directly
355
+ Array . from ( this . importedComponents . keys ( ) ) . forEach ( key => {
356
+ removeObjectKey ( this . code , templateProperties . components . value , key ) ;
357
+ } ) ;
358
+ } else {
359
+ // remove the entire components portion of the export
360
+ removeObjectKey ( this . code , defaultExport . declaration , 'components' ) ;
361
+ }
362
+ }
363
+
364
+ // Remove these after version 2
365
+ if ( templateProperties . onrender ) {
366
+ const { key } = templateProperties . onrender ;
367
+ this . code . overwrite ( key . start , key . end , 'oncreate' , true ) ;
368
+ templateProperties . oncreate = templateProperties . onrender ;
369
+ }
370
+
371
+ if ( templateProperties . onteardown ) {
372
+ const { key } = templateProperties . onteardown ;
373
+ this . code . overwrite ( key . start , key . end , 'ondestroy' , true ) ;
374
+ templateProperties . ondestroy = templateProperties . onteardown ;
375
+ }
376
+
377
+ // in an SSR context, we don't need to include events, methods, oncreate or ondestroy
378
+ if ( ssr ) {
379
+ if ( templateProperties . oncreate ) removeNode ( this . code , defaultExport . declaration , templateProperties . oncreate ) ;
380
+ if ( templateProperties . ondestroy ) removeNode ( this . code , defaultExport . declaration , templateProperties . ondestroy ) ;
381
+ if ( templateProperties . methods ) removeNode ( this . code , defaultExport . declaration , templateProperties . methods ) ;
382
+ if ( templateProperties . events ) removeNode ( this . code , defaultExport . declaration , templateProperties . events ) ;
383
+ }
384
+
385
+ // now that we've analysed the default export, we can determine whether or not we need to keep it
386
+ let hasDefaultExport = ! ! defaultExport ;
387
+ if ( defaultExport && defaultExport . declaration . properties . length === 0 ) {
388
+ hasDefaultExport = false ;
389
+ removeNode ( this . code , js . content , defaultExport ) ;
390
+ }
391
+
392
+ // if we do need to keep it, then we need to generate a return statement
393
+ if ( hasDefaultExport ) {
394
+ const finalNode = body [ body . length - 1 ] ;
395
+ if ( defaultExport === finalNode ) {
396
+ // export is last property, we can just return it
397
+ this . code . overwrite ( defaultExport . start , defaultExport . declaration . start , `return ` ) ;
398
+ } else {
399
+ const { declarations } = annotateWithScopes ( js ) ;
400
+ let template = 'template' ;
401
+ for ( let i = 1 ; declarations . has ( template ) ; template = `template_${ i ++ } ` ) ;
402
+
403
+ this . code . overwrite ( defaultExport . start , defaultExport . declaration . start , `var ${ template } = ` ) ;
404
+
405
+ let i = defaultExport . start ;
406
+ while ( / \s / . test ( source [ i - 1 ] ) ) i -- ;
407
+
408
+ const indentation = source . slice ( i , defaultExport . start ) ;
409
+ this . code . appendLeft ( finalNode . end , `\n\n${ indentation } return ${ template } ;` ) ;
410
+ }
411
+ }
412
+
413
+ // user code gets wrapped in an IIFE
414
+ if ( js . content . body . length ) {
415
+ const prefix = hasDefaultExport ? `var ${ this . alias ( 'template' ) } = (function () {` : `(function () {` ;
416
+ this . code . prependRight ( js . content . start , prefix ) . appendLeft ( js . content . end , '}());' ) ;
417
+ }
418
+
419
+ // if there's no need to include user code, remove it altogether
420
+ else {
421
+ this . code . remove ( js . content . start , js . content . end ) ;
422
+ hasJs = false ;
423
+ }
356
424
}
357
425
358
426
return {
359
427
computations,
360
- defaultExport,
428
+ hasJs,
429
+ namespace,
361
430
templateProperties
362
431
} ;
363
432
}
0 commit comments