1
1
import marked from 'https://esm.sh/[email protected] '
2
+ import postcss , { AcceptedPlugin } from 'https://esm.sh/[email protected] '
2
3
import { minify } from 'https://esm.sh/[email protected] '
3
4
import { safeLoadFront } from 'https://esm.sh/[email protected] '
4
5
import { Request } from './api.ts'
@@ -16,9 +17,9 @@ import { version } from './version.ts'
16
17
interface Module {
17
18
id : string
18
19
url : string
20
+ loader : string
19
21
isRemote : boolean
20
22
sourceFilePath : string
21
- sourceType : string
22
23
sourceHash : string
23
24
deps : { url : string , hash : string , async ?: boolean } [ ]
24
25
jsFile : string
@@ -53,6 +54,7 @@ export class Project {
53
54
#fsWatchListeners: Array < EventEmitter > = [ ]
54
55
#renderer: Renderer = { renderPage : ( ) => void 0 , renderHead : ( ) => void 0 }
55
56
#rendered: Map < string , Map < string , RenderResult > > = new Map ( )
57
+ #postcssPlugins: Record < string , AcceptedPlugin > = { }
56
58
57
59
constructor ( dir : string , mode : 'development' | 'production' , reload = false ) {
58
60
this . mode = mode
@@ -62,15 +64,22 @@ export class Project {
62
64
outputDir : '/dist' ,
63
65
baseUrl : '/' ,
64
66
defaultLocale : 'en' ,
67
+ env : { } ,
65
68
locales : [ ] ,
66
69
ssr : {
67
70
fallback : '_fallback.html'
68
71
} ,
69
72
buildTarget : mode === 'development' ? 'es2018' : 'es2015' ,
70
73
sourceMap : false ,
71
- env : { } ,
72
74
reactUrl :
'https://esm.sh/[email protected] ' ,
73
- reactDomUrl :
'https://esm.sh/[email protected] '
75
+ reactDomUrl :
'https://esm.sh/[email protected] ' ,
76
+ plugins : [ ] ,
77
+ postcss : {
78
+ plugins : [
79
+ 'postcss-flexbugs-fixes' ,
80
+ 'autoprefixer'
81
+ ]
82
+ }
74
83
}
75
84
this . importMap = { imports : { } }
76
85
this . ready = ( async ( ) => {
@@ -372,14 +381,14 @@ export class Project {
372
381
// write modules
373
382
const { sourceMap } = this . config
374
383
await Promise . all ( Array . from ( outputModules ) . map ( ( moduleID ) => {
375
- const { sourceFilePath, sourceType , isRemote, jsContent, jsSourceMap, hash } = this . #modules. get ( moduleID ) !
384
+ const { sourceFilePath, loader , isRemote, jsContent, jsSourceMap, hash } = this . #modules. get ( moduleID ) !
376
385
const saveDir = path . join ( distDir , path . dirname ( sourceFilePath ) )
377
386
const name = path . basename ( sourceFilePath ) . replace ( reModuleExt , '' )
378
387
const jsFile = path . join ( saveDir , name + ( isRemote ? '' : '.' + hash . slice ( 0 , hashShort ) ) ) + '.js'
379
388
if ( isRemote ) {
380
389
logModule ( 'deps' , jsContent . length )
381
390
} else {
382
- if ( sourceType === 'css' || sourceType === 'less ') {
391
+ if ( loader === 'css' ) {
383
392
logModule ( 'styles' , jsContent . length )
384
393
} else {
385
394
logModule ( 'modules' , jsContent . length )
@@ -415,7 +424,7 @@ export class Project {
415
424
416
425
const config : Record < string , any > = { }
417
426
for ( const name of Array . from ( [ 'aleph.config' , 'config' ] ) . map ( name => [ 'ts' , 'js' , 'mjs' , 'json' ] . map ( ext => `${ name } .${ ext } ` ) ) . flat ( ) ) {
418
- const p = path . join ( this . srcDir , name )
427
+ const p = path . join ( this . appRoot , name )
419
428
if ( existsFileSync ( p ) ) {
420
429
if ( name . endsWith ( '.json' ) ) {
421
430
const conf = JSON . parse ( await Deno . readTextFile ( p ) )
@@ -446,7 +455,9 @@ export class Project {
446
455
defaultLocale,
447
456
locales,
448
457
ssr,
449
- env
458
+ env,
459
+ plugins,
460
+ postcss,
450
461
} = config
451
462
if ( util . isNEString ( srcDir ) ) {
452
463
Object . assign ( this . config , { srcDir : util . cleanPath ( srcDir ) } )
@@ -482,10 +493,37 @@ export class Project {
482
493
if ( util . isPlainObject ( env ) ) {
483
494
Object . assign ( this . config , { env } )
484
495
}
485
- // Update buildID
496
+ if ( util . isNEArray ( plugins ) ) {
497
+ Object . assign ( this . config , { plugins } )
498
+ }
499
+ if ( util . isPlainObject ( postcss ) && util . isArray ( postcss . plugins ) ) {
500
+ Object . assign ( this . config , { postcss } )
501
+ } else if ( existsFileSync ( path . join ( this . appRoot , 'postcss.config.json' ) ) ) {
502
+ const text = await Deno . readTextFile ( path . join ( this . appRoot , 'postcss.config.json' ) )
503
+ try {
504
+ const postcss = JSON . parse ( text )
505
+ if ( util . isPlainObject ( postcss ) && util . isArray ( postcss . plugins ) ) {
506
+ Object . assign ( this . config , { postcss } )
507
+ }
508
+ } catch ( e ) {
509
+ log . warn ( 'bad postcss.config.json' , e . message )
510
+ }
511
+ }
512
+ // update buildID
486
513
Object . assign ( this , { buildID : this . mode + '.' + this . config . buildTarget } )
487
- // Update routing options.
514
+ // update routing options
488
515
this . #routing = new Routing ( [ ] , this . config . baseUrl , this . config . defaultLocale , this . config . locales )
516
+ // import post plugins
517
+ this . config . postcss . plugins . map ( async p => {
518
+ let name : string
519
+ if ( typeof p === 'string' ) {
520
+ name = p
521
+ } else {
522
+ name = p . name
523
+ }
524
+ const { default :
Plugin } = await import ( `https://esm.sh/${ name } [email protected] ` )
525
+ this . #postcssPlugins[ name ] = Plugin
526
+ } )
489
527
}
490
528
491
529
private async _init ( reload : boolean ) {
@@ -721,12 +759,22 @@ export class Project {
721
759
const isRemote = reHttp . test ( url ) || ( url in this . importMap . imports && reHttp . test ( this . importMap . imports [ url ] ) )
722
760
const sourceFilePath = renameImportUrl ( url )
723
761
const id = ( isRemote ? '//' + util . trimPrefix ( sourceFilePath , '/-/' ) : sourceFilePath ) . replace ( reModuleExt , '.js' )
762
+ let loader = ''
763
+ if ( reStyleModuleExt . test ( url ) ) {
764
+ loader = 'css'
765
+ } else if ( reMDExt . test ( url ) ) {
766
+ loader = 'markdown'
767
+ } else if ( reModuleExt . test ( url ) ) {
768
+ loader = 'js'
769
+ } else if ( isRemote ) {
770
+ loader = 'js'
771
+ }
724
772
return {
725
773
id,
726
774
url,
775
+ loader,
727
776
isRemote,
728
777
sourceFilePath,
729
- sourceType : path . extname ( sourceFilePath ) . slice ( 1 ) . replace ( 'mjs' , 'js' ) || 'js' ,
730
778
sourceHash : '' ,
731
779
deps : [ ] ,
732
780
jsFile : '' ,
@@ -863,14 +911,6 @@ export class Project {
863
911
if ( resp . status != 200 ) {
864
912
throw new Error ( `Download ${ url } : ${ resp . status } - ${ resp . statusText } ` )
865
913
}
866
- if ( mod . sourceType === 'js' ) {
867
- const t = resp . headers . get ( 'Content-Type' )
868
- if ( t ?. startsWith ( 'text/typescript' ) ) {
869
- mod . sourceType = 'ts'
870
- } else if ( t ?. startsWith ( 'text/jsx' ) ) {
871
- mod . sourceType = 'jsx'
872
- }
873
- }
874
914
mod . sourceHash = getHash ( sourceContent )
875
915
sourceContent = await resp . text ( )
876
916
shouldCompile = true
@@ -922,9 +962,9 @@ export class Project {
922
962
if ( shouldCompile ) {
923
963
const t = performance . now ( )
924
964
mod . deps = [ ]
925
- if ( mod . sourceType === 'css' || mod . sourceType === 'less ') {
965
+ if ( mod . loader === 'css' ) {
926
966
let css : string = sourceContent
927
- if ( mod . sourceType === ' less') {
967
+ if ( mod . id . endsWith ( '. less') ) {
928
968
try {
929
969
// todo: sourceMap
930
970
const output = await less . render ( sourceContent || '/* empty content */' )
@@ -933,6 +973,15 @@ export class Project {
933
973
throw new Error ( `less: ${ error } ` ) ;
934
974
}
935
975
}
976
+ const plugins = this . config . postcss . plugins . map ( p => {
977
+ if ( typeof p === 'string' ) {
978
+ return this . #postcssPlugins[ p ]
979
+ } else {
980
+ const Plugin = this . #postcssPlugins[ p . name ] as Function
981
+ return Plugin ( p . options )
982
+ }
983
+ } )
984
+ css = ( await postcss ( plugins ) . process ( css ) . async ( ) ) . content
936
985
if ( this . isDev ) {
937
986
css = String ( css ) . trim ( )
938
987
} else {
@@ -948,11 +997,7 @@ export class Project {
948
997
] . join ( this . isDev ? '\n' : '' )
949
998
mod . jsSourceMap = ''
950
999
mod . hash = getHash ( css )
951
- } else if ( mod . sourceType === 'sass' || mod . sourceType === 'scss' ) {
952
- // todo: support sass
953
- } else if ( mod . sourceType === 'mdx' ) {
954
- // todo: support mdx
955
- } else if ( mod . sourceType === 'md' || mod . sourceType === 'markdown' ) {
1000
+ } else if ( mod . loader === 'markdown' ) {
956
1001
const { __content, ...props } = safeLoadFront ( sourceContent )
957
1002
const html = marked . parse ( __content )
958
1003
mod . jsContent = [
@@ -987,7 +1032,7 @@ export class Project {
987
1032
] . filter ( Boolean ) . map ( l => ! this . isDev ? String ( l ) . trim ( ) : l ) . join ( this . isDev ? '\n' : '' )
988
1033
mod . jsSourceMap = ''
989
1034
mod . hash = getHash ( mod . jsContent )
990
- } else {
1035
+ } else if ( mod . loader === 'js' ) {
991
1036
const useDenos : string [ ] = [ ]
992
1037
const compileOptions = {
993
1038
mode : this . mode ,
@@ -1032,6 +1077,8 @@ export class Project {
1032
1077
useDenos . forEach ( sig => {
1033
1078
mod . deps . push ( { url : '#' + sig , hash : '' , async : true } )
1034
1079
} )
1080
+ } else {
1081
+ throw new Error ( `Unknown loader '${ mod . loader } '` )
1035
1082
}
1036
1083
1037
1084
log . debug ( `compile '${ url } ' in ${ Math . round ( performance . now ( ) - t ) } ms` )
@@ -1210,8 +1257,8 @@ export class Project {
1210
1257
host : 'localhost' ,
1211
1258
hostname : 'localhost' ,
1212
1259
port : '' ,
1213
- href : 'http ://localhost ' + url . pathname + url . query . toString ( ) ,
1214
- origin : 'http ://localhost ' ,
1260
+ href : 'https ://esm.sh ' + url . pathname + url . query . toString ( ) ,
1261
+ origin : 'https ://esm.sh ' ,
1215
1262
pathname : url . pathname ,
1216
1263
search : url . query . toString ( ) ,
1217
1264
hash : '' ,
0 commit comments