@@ -4,147 +4,140 @@ import { toLocalPath, computeHash } from '../server/helper.ts'
4
4
import { existsFile } from '../shared/fs.ts'
5
5
import { Measure } from '../shared/log.ts'
6
6
import util from '../shared/util.ts'
7
- import type { ModuleLoader , Plugin , PostCSSPlugin } from '../types.ts'
7
+ import type { Aleph , LoadInput , LoadOutput , Plugin , PostCSSPlugin } from '../types.ts'
8
8
9
+ const test = / \. ( c s s | p c s s | p o s t c s s ) $ / i
9
10
const postcssVersion = '8.3.5'
10
11
const postcssModulesVersion = '4.1.3'
11
12
const productionOnlyPostcssPlugins = [ 'autoprefixer' ]
12
13
const isModulesPluginName = ( v : any ) : v is string => ( typeof v === 'string' && / ^ p o s t c s s \- m o d u l e s ( @ | $ ) / i. test ( v . trim ( ) ) )
13
14
14
- /** the builtin css loader */
15
- export const builtinCSSLoader : Readonly < ModuleLoader > = {
16
- test : / \. ( c s s | p c s s | p o s t c s s ) $ / i,
17
- acceptHMR : true ,
18
- load : async ( { specifier, data } , app ) => {
19
- const ms = new Measure ( )
20
- const { css : cssConfig } = app . config
21
- const isRemote = util . isLikelyHttpURL ( specifier )
22
-
23
- if ( isRemote && specifier . endsWith ( '.css' ) && ! cssConfig . cache ) {
24
- return {
25
- code : [
26
- `import { applyCSS } from "https://deno.land/x/aleph/framework/core/style.ts"` ,
27
- `export const href = ${ JSON . stringify ( specifier ) } ` ,
28
- `export default {}` ,
29
- `applyCSS(${ JSON . stringify ( specifier ) } , { href })` ,
30
- ] . join ( '\n' )
31
- }
32
- }
15
+ /** builtin css loader */
16
+ export const cssLoader = async ( { specifier, data } : LoadInput , aleph : Aleph ) : Promise < LoadOutput > => {
17
+ const ms = new Measure ( )
18
+ const { css : cssConfig } = aleph . config
19
+ const isRemote = util . isLikelyHttpURL ( specifier )
33
20
34
- // Don't process .css files in ./public folder
35
- if ( ! isRemote && specifier . endsWith ( '.css' ) && await existsFile ( join ( app . workingDir , 'public' , specifier ) ) ) {
36
- return {
37
- code : [
38
- `import { applyCSS } from "https://deno.land/x/aleph/framework/core/style.ts"` ,
39
- `export const href = ${ JSON . stringify ( specifier ) } ` ,
40
- `export default {}` ,
41
- `applyCSS(${ JSON . stringify ( specifier ) } , { href })` ,
42
- ] . join ( '\n' )
43
- }
21
+ if ( isRemote && specifier . endsWith ( '.css' ) && ! cssConfig . cache ) {
22
+ return {
23
+ code : [
24
+ `import { applyCSS } from "https://deno.land/x/aleph/framework/core/style.ts"` ,
25
+ `export const href = ${ JSON . stringify ( specifier ) } ` ,
26
+ `export default {}` ,
27
+ `applyCSS(${ JSON . stringify ( specifier ) } , { href })` ,
28
+ ] . join ( '\n' )
44
29
}
30
+ }
45
31
46
- let plugins = cssConfig . postcss ?. plugins || [ ]
47
- let modulesJSON : Record < string , string > = { }
48
- if ( / \. m o d u l e \. [ a - z ] + $ / . test ( specifier ) ) {
49
- const options = {
50
- ...( util . isPlainObject ( cssConfig . modules ) ? cssConfig . modules : { } ) ,
51
- getJSON : ( _specifier : string , json : Record < string , string > ) => {
52
- modulesJSON = json
53
- } ,
54
- }
55
- let hasModulesPlugin = false
56
- plugins = plugins . map ( plugin => {
57
- if ( isModulesPluginName ( plugin ) ) {
58
- hasModulesPlugin = true
59
- return [ plugin . trim ( ) . toLowerCase ( ) , options ]
60
- }
61
- if ( Array . isArray ( plugin ) && isModulesPluginName ( plugin [ 0 ] ) ) {
62
- hasModulesPlugin = true
63
- return [ plugin [ 0 ] . trim ( ) . toLowerCase ( ) , { ...options , ...plugin [ 1 ] } ]
64
- }
65
- return plugin
66
- } )
67
-
68
- if ( ! hasModulesPlugin ) {
69
- plugins . push ( [ `postcss-modules@${ postcssModulesVersion } ` , options ] )
70
- }
32
+ // Don't process .css files in ./public folder
33
+ if ( ! isRemote && specifier . endsWith ( '.css' ) && await existsFile ( join ( aleph . workingDir , 'public' , specifier ) ) ) {
34
+ return {
35
+ code : [
36
+ `import { applyCSS } from "https://deno.land/x/aleph/framework/core/style.ts"` ,
37
+ `export const href = ${ JSON . stringify ( specifier ) } ` ,
38
+ `export default {}` ,
39
+ `applyCSS(${ JSON . stringify ( specifier ) } , { href })` ,
40
+ ] . join ( '\n' )
71
41
}
72
- const postcss = await initPostCSS ( plugins , app . mode === 'development' )
73
-
74
- let sourceCode = ''
75
- let css = ''
42
+ }
76
43
77
- if ( data instanceof Uint8Array ) {
78
- sourceCode = ( new TextDecoder ) . decode ( data )
79
- } else if ( util . isFilledString ( data ) ) {
80
- sourceCode = data
81
- } else {
82
- const { content } = await app . fetchModule ( specifier )
83
- sourceCode = ( new TextDecoder ) . decode ( content )
44
+ let plugins = cssConfig . postcss ?. plugins || [ ]
45
+ let modulesJSON : Record < string , string > = { }
46
+ if ( / \. m o d u l e \. [ a - z ] + $ / . test ( specifier ) ) {
47
+ const options = {
48
+ ...( util . isPlainObject ( cssConfig . modules ) ? cssConfig . modules : { } ) ,
49
+ getJSON : ( _specifier : string , json : Record < string , string > ) => {
50
+ modulesJSON = json
51
+ } ,
84
52
}
85
-
86
- // do not process remote css files
87
- if ( isRemote && specifier . endsWith ( '.css' ) ) {
88
- css = sourceCode
89
- } else {
90
- const ret = await postcss . process ( sourceCode , { from : specifier } ) . async ( )
91
- css = ret . css
53
+ let hasModulesPlugin = false
54
+ plugins = plugins . map ( plugin => {
55
+ if ( isModulesPluginName ( plugin ) ) {
56
+ hasModulesPlugin = true
57
+ return [ plugin . trim ( ) . toLowerCase ( ) , options ]
58
+ }
59
+ if ( Array . isArray ( plugin ) && isModulesPluginName ( plugin [ 0 ] ) ) {
60
+ hasModulesPlugin = true
61
+ return [ plugin [ 0 ] . trim ( ) . toLowerCase ( ) , { ...options , ...plugin [ 1 ] } ]
62
+ }
63
+ return plugin
64
+ } )
65
+ if ( ! hasModulesPlugin ) {
66
+ plugins . push ( [ `postcss-modules@${ postcssModulesVersion } ` , options ] )
92
67
}
68
+ }
69
+ const postcss = await initPostCSS ( plugins , aleph . mode === 'development' )
70
+
71
+ let sourceCode = ''
72
+ let css = ''
73
+
74
+ if ( data instanceof Uint8Array ) {
75
+ sourceCode = ( new TextDecoder ) . decode ( data )
76
+ } else if ( util . isFilledString ( data ) ) {
77
+ sourceCode = data
78
+ } else {
79
+ const { content } = await aleph . fetchModule ( specifier )
80
+ sourceCode = ( new TextDecoder ) . decode ( content )
81
+ }
93
82
94
- if ( app . mode === 'production' ) {
95
- const ret = await esbuild ( {
96
- stdin : {
97
- loader : 'css' ,
98
- sourcefile : specifier ,
99
- contents : css
100
- } ,
101
- bundle : false ,
102
- minify : true ,
103
- write : false
104
- } )
105
- css = util . trimSuffix ( ret . outputFiles [ 0 ] . text , '\n' )
106
- }
83
+ // do not process remote css files
84
+ if ( isRemote && specifier . endsWith ( '.css' ) ) {
85
+ css = sourceCode
86
+ } else {
87
+ const ret = await postcss . process ( sourceCode , { from : specifier } ) . async ( )
88
+ css = ret . css
89
+ }
107
90
108
- ms . stop ( `process ${ specifier } ` )
91
+ if ( aleph . mode === 'production' ) {
92
+ const ret = await esbuild ( {
93
+ stdin : {
94
+ loader : 'css' ,
95
+ sourcefile : specifier ,
96
+ contents : css
97
+ } ,
98
+ bundle : false ,
99
+ minify : true ,
100
+ write : false
101
+ } )
102
+ css = util . trimSuffix ( ret . outputFiles [ 0 ] . text , '\n' )
103
+ }
109
104
110
- if ( specifier . startsWith ( '#inline-style-' ) ) {
111
- return { type : 'css' , code : css }
112
- }
105
+ ms . stop ( `process ${ specifier } ` )
113
106
114
- const { extract } = cssConfig
115
- if ( extract && ( extract === true || css . length > ( extract . limit || 8 * 1024 ) ) ) {
116
- const ext = extname ( specifier )
117
- const hash = computeHash ( css ) . slice ( 0 , 8 )
118
- const path = util . trimSuffix ( isRemote ? toLocalPath ( specifier ) : specifier , ext ) + '.' + hash + ext
119
- await app . addDist ( path , ( new TextEncoder ) . encode ( css ) )
120
- return {
121
- code : [
122
- `import { applyCSS } from "https://deno.land/x/aleph/framework/core/style.ts"` ,
123
- `export const href = ${ JSON . stringify ( '/_aleph/' + util . trimPrefix ( path , '/' ) ) } ` ,
124
- `export default ${ JSON . stringify ( modulesJSON ) } ` ,
125
- `applyCSS(${ JSON . stringify ( specifier ) } , { href })`
126
- ] . join ( '\n' ) ,
127
- // todo: generate map
128
- }
129
- }
107
+ if ( specifier . startsWith ( '#inline-style-' ) ) {
108
+ return { type : 'css' , code : css }
109
+ }
130
110
111
+ const { extract } = cssConfig
112
+ if ( extract && ( extract === true || css . length > ( extract . limit || 8 * 1024 ) ) ) {
113
+ const ext = extname ( specifier )
114
+ const hash = computeHash ( css ) . slice ( 0 , 8 )
115
+ const path = util . trimSuffix ( isRemote ? toLocalPath ( specifier ) : specifier , ext ) + '.' + hash + ext
116
+ await aleph . addDist ( path , ( new TextEncoder ) . encode ( css ) )
131
117
return {
132
118
code : [
133
119
`import { applyCSS } from "https://deno.land/x/aleph/framework/core/style.ts"` ,
134
- `export const css = ${ JSON . stringify ( css ) } ` ,
120
+ `export const href = ${ JSON . stringify ( '/_aleph/' + util . trimPrefix ( path , '/' ) ) } ` ,
135
121
`export default ${ JSON . stringify ( modulesJSON ) } ` ,
136
- `applyCSS(${ JSON . stringify ( specifier ) } , { css })` ,
122
+ `applyCSS(${ JSON . stringify ( specifier ) } , { href })`
137
123
] . join ( '\n' ) ,
138
124
// todo: generate map
139
125
}
140
126
}
141
- }
142
127
143
- /** check whether the loader is the builtin css loader */
144
- export function isBuiltinCSSLoader ( loader : ModuleLoader ) : boolean {
145
- return loader === builtinCSSLoader
128
+ return {
129
+ code : [
130
+ `import { applyCSS } from "https://deno.land/x/aleph/framework/core/style.ts"` ,
131
+ `export const css = ${ JSON . stringify ( css ) } ` ,
132
+ `export default ${ JSON . stringify ( modulesJSON ) } ` ,
133
+ `applyCSS(${ JSON . stringify ( specifier ) } , { css })` ,
134
+ ] . join ( '\n' ) ,
135
+ // todo: generate map
136
+ }
146
137
}
147
138
139
+ export const isCSS = ( specifier : string ) : boolean => test . test ( specifier )
140
+
148
141
async function initPostCSS ( plugins : PostCSSPlugin [ ] , isDev : boolean ) {
149
142
const pluginObjects = await Promise . all ( plugins . filter ( p => {
150
143
if ( isDev ) {
@@ -192,6 +185,9 @@ async function importPostcssPluginByName(name: string) {
192
185
export default ( ) : Plugin => {
193
186
return {
194
187
name : 'css-loader' ,
195
- setup : ( aleph ) => aleph . addModuleLoader ( builtinCSSLoader )
188
+ setup : aleph => {
189
+ aleph . onResolve ( test , ( ) => ( { acceptHMR : true } ) )
190
+ aleph . onLoad ( test , input => cssLoader ( input , aleph ) )
191
+ }
196
192
}
197
193
}
0 commit comments