Skip to content
This repository was archived by the owner on Jul 6, 2025. It is now read-only.

Commit 16483f9

Browse files
committed
rewrite module loader api
1 parent 080979e commit 16483f9

File tree

10 files changed

+464
-432
lines changed

10 files changed

+464
-432
lines changed

framework/react/init.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ export async function init(aleph: Aleph) {
66
const alephPkgUri = getAlephPkgUri()
77
const alephPkgPath = alephPkgUri.replace('https://', '').replace('http://localhost:', 'http_localhost_')
88
await aleph.addModule(`${alephPkgUri}/framework/react/refresh.ts`)
9-
aleph.injectCode('compilation', '/main.js', (_: string, code: string) => ({
9+
aleph.onTransform('main.js', ({ code }) => ({
1010
code: [
1111
`import "./-/${alephPkgPath}/framework/react/refresh.js";`,
1212
code
1313
].join('\n')
1414
}))
15-
aleph.injectCode('hmr', (specifier: string, code: string) => ({
15+
aleph.onTransform('hmr', ({ specifier, code }) => ({
1616
code: code.includes('$RefreshReg$(') ? [
1717
'const prevRefreshReg = $RefreshReg$;',
1818
'const prevRefreshSig = $RefreshSig$;',

plugins/css.ts

Lines changed: 109 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -4,147 +4,140 @@ import { toLocalPath, computeHash } from '../server/helper.ts'
44
import { existsFile } from '../shared/fs.ts'
55
import { Measure } from '../shared/log.ts'
66
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'
88

9+
const test = /\.(css|pcss|postcss)$/i
910
const postcssVersion = '8.3.5'
1011
const postcssModulesVersion = '4.1.3'
1112
const productionOnlyPostcssPlugins = ['autoprefixer']
1213
const isModulesPluginName = (v: any): v is string => (typeof v === 'string' && /^postcss\-modules(@|$)/i.test(v.trim()))
1314

14-
/** the builtin css loader */
15-
export const builtinCSSLoader: Readonly<ModuleLoader> = {
16-
test: /\.(css|pcss|postcss)$/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)
3320

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')
4429
}
30+
}
4531

46-
let plugins = cssConfig.postcss?.plugins || []
47-
let modulesJSON: Record<string, string> = {}
48-
if (/\.module\.[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')
7141
}
72-
const postcss = await initPostCSS(plugins, app.mode === 'development')
73-
74-
let sourceCode = ''
75-
let css = ''
42+
}
7643

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 (/\.module\.[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+
},
8452
}
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])
9267
}
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+
}
9382

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+
}
10790

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+
}
109104

110-
if (specifier.startsWith('#inline-style-')) {
111-
return { type: 'css', code: css }
112-
}
105+
ms.stop(`process ${specifier}`)
113106

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+
}
130110

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))
131117
return {
132118
code: [
133119
`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, '/'))}`,
135121
`export default ${JSON.stringify(modulesJSON)}`,
136-
`applyCSS(${JSON.stringify(specifier)}, { css })`,
122+
`applyCSS(${JSON.stringify(specifier)}, { href })`
137123
].join('\n'),
138124
// todo: generate map
139125
}
140126
}
141-
}
142127

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+
}
146137
}
147138

139+
export const isCSS = (specifier: string): boolean => test.test(specifier)
140+
148141
async function initPostCSS(plugins: PostCSSPlugin[], isDev: boolean) {
149142
const pluginObjects = await Promise.all(plugins.filter(p => {
150143
if (isDev) {
@@ -192,6 +185,9 @@ async function importPostcssPluginByName(name: string) {
192185
export default (): Plugin => {
193186
return {
194187
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+
}
196192
}
197193
}

plugins/css_test.ts

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { stopEsbuild } from '../bundler/esbuild.ts'
66
import { Aleph } from '../server/aleph.ts'
77
import { computeHash } from '../server/helper.ts'
88
import { ensureTextFile } from '../shared/fs.ts'
9-
import { builtinCSSLoader } from './css.ts'
9+
import { isCSS, cssLoader } from './css.ts'
1010

1111
Deno.test('plugin: css loader', async () => {
1212
Deno.env.set('DENO_TESTING', 'true')
@@ -16,19 +16,18 @@ Deno.test('plugin: css loader', async () => {
1616
join(dir, '/style/index.css'),
1717
'h1 { font-size: 18px; }'
1818
)
19-
const { code } = await builtinCSSLoader.load!({ specifier: '/style/index.css', }, aleph)
20-
assert(builtinCSSLoader.test.test('/style/index.css'))
21-
assert(builtinCSSLoader.test.test('/style/index.pcss'))
22-
assert(builtinCSSLoader.test.test('/style/index.postcss'))
23-
assert(!builtinCSSLoader.test.test('/style/index.less'))
24-
assert(!builtinCSSLoader.test.test('/style/index.sass'))
25-
assert(builtinCSSLoader.acceptHMR)
19+
const { code } = await cssLoader({ specifier: '/style/index.css', }, aleph)
2620
assertEquals(code, [
2721
'import { applyCSS } from "https://deno.land/x/aleph/framework/core/style.ts"',
2822
'export const css = "h1 { font-size: 18px; }"',
2923
'export default {}',
3024
'applyCSS("/style/index.css", { css })',
3125
].join('\n'))
26+
assert(isCSS('/style/index.css'))
27+
assert(isCSS('/style/index.pcss'))
28+
assert(isCSS('/style/index.postcss'))
29+
assert(!isCSS('/style/index.less'))
30+
assert(!isCSS('/style/index.sass'))
3231
})
3332

3433
Deno.test({
@@ -41,7 +40,7 @@ Deno.test({
4140
join(dir, '/style/index.css'),
4241
'h1 { font-size: 18px; }'
4342
)
44-
const { code } = await builtinCSSLoader.load!({ specifier: '/style/index.css', }, aleph)
43+
const { code } = await cssLoader({ specifier: '/style/index.css', }, aleph)
4544
assertEquals(code, [
4645
'import { applyCSS } from "https://deno.land/x/aleph/framework/core/style.ts"',
4746
'export const css = "h1{font-size:18px}"',
@@ -64,7 +63,7 @@ Deno.test('plugin: css loader with extract size option', async () => {
6463
join(dir, '/style/index.css'),
6564
'h1 { font-size: 18px; }'
6665
)
67-
const { code } = await builtinCSSLoader.load!({ specifier: '/style/index.css', }, aleph)
66+
const { code } = await cssLoader({ specifier: '/style/index.css', }, aleph)
6867
const distPath = `/style/index.${computeHash('h1 { font-size: 18px; }').slice(0, 8)}.css`
6968
assertEquals(code, [
7069
'import { applyCSS } from "https://deno.land/x/aleph/framework/core/style.ts"',
@@ -79,7 +78,7 @@ Deno.test('plugin: css loader for remote external', async () => {
7978
Deno.env.set('DENO_TESTING', 'true')
8079
const dir = await Deno.makeTempDir({ prefix: 'aleph_plugin_testing' })
8180
const aleph = new Aleph(dir, 'development')
82-
const { code } = await builtinCSSLoader.load!({ specifier: 'https://esm.sh/tailwindcss/dist/tailwind.min.css', }, aleph)
81+
const { code } = await cssLoader({ specifier: 'https://esm.sh/tailwindcss/dist/tailwind.min.css', }, aleph)
8382
assertEquals(code, [
8483
'import { applyCSS } from "https://deno.land/x/aleph/framework/core/style.ts"',
8584
'export const href = "https://esm.sh/tailwindcss/dist/tailwind.min.css"',
@@ -92,7 +91,7 @@ Deno.test('plugin: css loader for inline styles', async () => {
9291
Deno.env.set('DENO_TESTING', 'true')
9392
const dir = await Deno.makeTempDir({ prefix: 'aleph_plugin_testing' })
9493
const aleph = new Aleph(dir, 'development')
95-
const { code, type } = await builtinCSSLoader.load!({
94+
const { code, type } = await cssLoader({
9695
specifier: '#inline-style-{}',
9796
data: 'h1 { font-size: 18px; }'
9897
}, aleph)
@@ -114,7 +113,7 @@ Deno.test({
114113
join(dir, '/style/index.module.css'),
115114
'.name { font-size: 18px; }'
116115
)
117-
const { code } = await builtinCSSLoader.load!({ specifier: '/style/index.module.css', }, aleph)
116+
const { code } = await cssLoader({ specifier: '/style/index.module.css', }, aleph)
118117
assertEquals(code, [
119118
'import { applyCSS } from "https://deno.land/x/aleph/framework/core/style.ts"',
120119
'export const css = ".index-module_name { font-size: 18px; }"',
@@ -136,7 +135,7 @@ Deno.test('plugin: css loader with postcss plugins', async () => {
136135
join(dir, '/style/index.css'),
137136
'.foo { .bar { font-size: 100%; } }'
138137
)
139-
const { code } = await builtinCSSLoader.load!({ specifier: '/style/index.css' }, aleph)
138+
const { code } = await cssLoader({ specifier: '/style/index.css' }, aleph)
140139
assertEquals(code, [
141140
'import { applyCSS } from "https://deno.land/x/aleph/framework/core/style.ts"',
142141
'export const css = ".foo .bar { font-size: 100%; }"',

0 commit comments

Comments
 (0)