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

Commit 910495c

Browse files
committed
Imporove css loading
1 parent 3f7be7f commit 910495c

File tree

6 files changed

+117
-69
lines changed

6 files changed

+117
-69
lines changed

bundler/esbuild.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,31 @@ export {
99
stop as stopEsbuild
1010
}
1111

12-
export const esbuildUrlLoader: Plugin = {
12+
export const esmLoader: Plugin = {
1313
name: 'esm-loader',
1414
setup(build) {
1515
build.onResolve({ filter: /.*/ }, args => {
16-
if (util.isLikelyHttpURL(args.path)) {
16+
const isRemote = util.isLikelyHttpURL(args.path)
17+
const path = isRemote ? args.path : util.trimPrefix(args.path, 'file://')
18+
19+
if (
20+
args.kind === 'url-token' ||
21+
(args.kind === 'import-rule' && (isRemote || path.startsWith('/')))
22+
) {
23+
return { path: path, external: true }
24+
}
25+
if (isRemote) {
1726
return {
18-
path: args.path,
27+
path,
1928
namespace: 'http-module',
2029
}
2130
}
2231
if (args.namespace === 'http-module') {
2332
return {
24-
path: (new URL(args.path, args.importer)).toString(),
33+
path: (new URL(path, args.importer)).toString(),
2534
namespace: 'http-module',
2635
}
2736
}
28-
const [path] = util.splitBy(util.trimPrefix(args.path, 'file://'), '#')
2937
if (path.startsWith('.')) {
3038
return { path: join(args.resolveDir, path) }
3139
}

bundler/mod.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { VERSION } from '../version.ts'
88
import type { DependencyGraph } from '../server/analyzer.ts'
99
import type { Aleph } from '../server/aleph.ts'
1010
import { clearBuildCache, computeHash, getAlephPkgUri } from '../server/helper.ts'
11-
import { esbuild, stopEsbuild, esbuildUrlLoader } from './esbuild.ts'
11+
import { esbuild, stopEsbuild, esmLoader } from './esbuild.ts'
1212

1313
export const bundlerRuntimeCode = `
1414
window.__ALEPH__ = {
@@ -254,14 +254,15 @@ export class Bundler {
254254
outfile: bundleFile,
255255
platform: 'browser',
256256
format: 'iife',
257-
target: [String(build.target)].concat(Object.keys(build.browsers).map(name => {
258-
return `${name}${build.browsers[name as BrowserName]}`
259-
})),
257+
target: [
258+
String(build.target),
259+
...Object.keys(build.browsers).map(name => `${name}${build.browsers[name as BrowserName]}`)
260+
],
260261
bundle: true,
261262
minify: true,
262263
treeShaking: true,
263264
sourcemap: false,
264-
plugins: [esbuildUrlLoader],
265+
plugins: [esmLoader],
265266
})
266267
}
267268
}

plugins/css.ts

Lines changed: 55 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { extname, join } from 'https://deno.land/[email protected]/path/mod.ts'
2-
import { esbuild } from '../bundler/esbuild.ts'
2+
import { esbuild, esmLoader } from '../bundler/esbuild.ts'
33
import { toLocalPath, computeHash } from '../server/helper.ts'
44
import { existsFile } from '../shared/fs.ts'
5-
import { Measure } from '../shared/log.ts'
5+
import log, { Measure } from '../shared/log.ts'
66
import util from '../shared/util.ts'
77
import type { Aleph, LoadInput, LoadOutput, Plugin, PostCSSPlugin } from '../types.d.ts'
88

@@ -18,7 +18,15 @@ export const cssLoader = async ({ specifier, data }: LoadInput, aleph: Aleph): P
1818
const { css: cssConfig } = aleph.config
1919
const isRemote = util.isLikelyHttpURL(specifier)
2020

21-
if (isRemote && specifier.endsWith('.css') && !cssConfig.cache) {
21+
// Don't process remote .css files if the cache is disabled
22+
if (
23+
(isRemote && specifier.endsWith('.css')) &&
24+
(
25+
cssConfig.cache === false ||
26+
(cssConfig.cache instanceof RegExp && !cssConfig.cache.test(specifier)) ||
27+
(Array.isArray(cssConfig.cache) && !cssConfig.cache.some(r => r.test(specifier)))
28+
)
29+
) {
2230
return {
2331
code: [
2432
`import { applyCSS } from "https://deno.land/x/aleph/framework/core/style.ts"`,
@@ -30,7 +38,12 @@ export const cssLoader = async ({ specifier, data }: LoadInput, aleph: Aleph): P
3038
}
3139

3240
// Don't process .css files in ./public folder
33-
if (!isRemote && specifier.endsWith('.css') && await existsFile(join(aleph.workingDir, 'public', specifier))) {
41+
if (
42+
!isRemote &&
43+
specifier.endsWith('.css') &&
44+
!(await existsFile(join(aleph.workingDir, specifier))) &&
45+
await existsFile(join(aleph.workingDir, 'public', specifier))
46+
) {
3447
return {
3548
code: [
3649
`import { applyCSS } from "https://deno.land/x/aleph/framework/core/style.ts"`,
@@ -41,7 +54,19 @@ export const cssLoader = async ({ specifier, data }: LoadInput, aleph: Aleph): P
4154
}
4255
}
4356

44-
let plugins = cssConfig.postcss.plugins || []
57+
let sourceCode = ''
58+
let css = ''
59+
60+
if (data instanceof Uint8Array) {
61+
sourceCode = (new TextDecoder).decode(data)
62+
} else if (util.isFilledString(data)) {
63+
sourceCode = data
64+
} else {
65+
const { content } = await aleph.fetchModule(specifier)
66+
sourceCode = (new TextDecoder).decode(content)
67+
}
68+
69+
let postPlugins = cssConfig.postcss.plugins || []
4570
let modulesJSON: Record<string, string> = {}
4671
if (/\.module\.[a-z]+$/.test(specifier)) {
4772
const options = {
@@ -51,7 +76,7 @@ export const cssLoader = async ({ specifier, data }: LoadInput, aleph: Aleph): P
5176
},
5277
}
5378
let hasModulesPlugin = false
54-
plugins = plugins.map(plugin => {
79+
postPlugins = postPlugins.map(plugin => {
5580
if (isModulesPluginName(plugin)) {
5681
hasModulesPlugin = true
5782
return [plugin.trim().toLowerCase(), options]
@@ -63,53 +88,48 @@ export const cssLoader = async ({ specifier, data }: LoadInput, aleph: Aleph): P
6388
return plugin
6489
})
6590
if (!hasModulesPlugin) {
66-
plugins.push([`postcss-modules@${postcssModulesVersion}`, options])
91+
postPlugins.push([`postcss-modules@${postcssModulesVersion}`, options])
6792
}
6893
}
69-
const postcss = await initPostCSS(plugins, aleph.mode === 'development')
70-
71-
let sourceCode = ''
72-
let css = ''
7394

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-
}
95+
// init postcss with plugins
96+
const postcss = await initPostCSS(postPlugins, aleph.mode === 'development')
8297

83-
// do not process remote css files
84-
if (isRemote && specifier.endsWith('.css')) {
98+
// postcss: don't process large(>64k) remote css files
99+
if (isRemote && specifier.endsWith('.css') && !specifier.endsWith('.module.css') && sourceCode.length > 64 * 1024) {
85100
css = sourceCode
86-
} else {
87-
const ret = await postcss.process(sourceCode, { from: specifier }).async()
88-
css = ret.css
101+
} else if (postcss !== null) {
102+
try {
103+
const ret = await postcss.process(sourceCode, { from: specifier }).async()
104+
css = ret.css
105+
} catch (err) {
106+
log.warn('postcss:', err.mesage)
107+
}
89108
}
90109

91-
if (aleph.mode === 'production') {
110+
try {
92111
const ret = await esbuild({
93112
stdin: {
94113
loader: 'css',
95114
sourcefile: specifier,
96115
contents: css
97116
},
98-
bundle: false,
99-
minify: true,
100-
write: false
117+
write: false,
118+
bundle: true,
119+
minify: aleph.mode === 'production',
120+
plugins: [esmLoader],
101121
})
102122
css = util.trimSuffix(ret.outputFiles[0].text, '\n')
103-
}
123+
} catch (e) { }
104124

105125
ms.stop(`process ${specifier}`)
106126

107127
if (specifier.startsWith('#inline-style-')) {
108128
return { type: 'css', code: css }
109129
}
110130

111-
const { extract } = cssConfig
112-
if (extract && (extract === true || css.length > (extract.limit || 8 * 1024))) {
131+
const { extract: { limit = 8 * 1024 } } = cssConfig
132+
if (css.length > limit) {
113133
const ext = extname(specifier)
114134
const hash = computeHash(css).slice(0, 8)
115135
const path = util.trimSuffix(isRemote ? toLocalPath(specifier) : specifier, ext) + '.' + hash + ext
@@ -139,7 +159,7 @@ export const cssLoader = async ({ specifier, data }: LoadInput, aleph: Aleph): P
139159
export const isCSS = (specifier: string): boolean => test.test(specifier)
140160

141161
async function initPostCSS(plugins: PostCSSPlugin[], isDev: boolean) {
142-
const pluginObjects = await Promise.all(plugins.filter(p => {
162+
const postPlugins = await Promise.all(plugins.filter(p => {
143163
if (isDev) {
144164
if (util.isFilledString(p) && productionOnlyPostcssPlugins.includes(p)) {
145165
return false
@@ -162,18 +182,12 @@ async function initPostCSS(plugins: PostCSSPlugin[], isDev: boolean) {
162182
}
163183
}))
164184

165-
if (pluginObjects.length === 0) {
166-
return {
167-
process: (content: string) => ({
168-
async: async () => {
169-
return { css: content }
170-
}
171-
})
172-
}
185+
if (postPlugins.length === 0) {
186+
return null
173187
}
174188

175189
const { default: PostCSS } = await import(`https://esm.sh/postcss@${postcssVersion}`)
176-
return PostCSS(pluginObjects)
190+
return PostCSS(postPlugins)
177191
}
178192

179193
async function importPostcssPluginByName(name: string) {

server/aleph.ts

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,10 @@ export class Aleph implements IAleph {
133133
})
134134
log.info('load env from', basename(p))
135135
}
136+
136137
Deno.env.set('ALEPH_ENV', this.#mode)
137138
Deno.env.set('ALEPH_FRAMEWORK', this.#config.framework)
139+
Deno.env.set('ALEPH_WORKING_DIR', this.#workingDir)
138140
Deno.env.set('ALEPH_VERSION', VERSION)
139141

140142
const alephPkgUri = getAlephPkgUri()
@@ -335,45 +337,55 @@ export class Aleph implements IAleph {
335337
this.clearSSRCache(specifier)
336338
})
337339
this.clearSSRCache(specifier)
338-
log.info('modify', specifier)
340+
log.debug('modify', specifier)
339341
} catch (err) {
340342
log.error(`compile(${specifier}):`, err.message)
341343
}
342344
} else {
343345
let routePath: string | undefined = undefined
344346
let isIndex: boolean | undefined = undefined
345-
let emit = false
347+
let unrouted = false
346348
if (this.isPageModule(specifier)) {
347-
let isNew = true
349+
unrouted = true
348350
this.#pageRouting.lookup(routes => {
349351
routes.forEach(({ module }) => {
350352
if (module === specifier) {
351-
isNew = false
353+
unrouted = false
352354
return false // break loop
353355
}
354356
})
355357
})
356-
if (isNew) {
358+
if (unrouted) {
357359
const [_routePath, _specifier, _isIndex] = this.createRouteUpdate(specifier)
358360
routePath = _routePath
359361
specifier = _specifier
360362
isIndex = _isIndex
361-
emit = true
362363
this.#pageRouting.update(routePath, specifier, isIndex)
363364
}
364365
} else if (specifier.startsWith('/api/') && !specifier.startsWith('/api/_middlewares.')) {
365-
this.#apiRouting.update(...this.createRouteUpdate(specifier))
366+
unrouted = true
367+
this.#pageRouting.lookup(routes => {
368+
routes.forEach(({ module }) => {
369+
if (module === specifier) {
370+
unrouted = false
371+
return false // break loop
372+
}
373+
})
374+
})
375+
if (unrouted) {
376+
this.#apiRouting.update(...this.createRouteUpdate(specifier))
377+
}
366378
}
367379
if (trimBuiltinModuleExts(specifier) === '/app') {
368380
await this.compile(specifier)
369-
emit = true
381+
unrouted = true
370382
}
371-
if (emit) {
383+
if (unrouted) {
372384
this.#fsWatchListeners.forEach(e => {
373385
e.emit('add', { specifier, routePath, isIndex })
374386
})
387+
log.debug('add', specifier)
375388
}
376-
log.info('add', specifier)
377389
}
378390
} else {
379391
if (this.#modules.has(specifier)) {
@@ -388,7 +400,7 @@ export class Aleph implements IAleph {
388400
this.#apiRouting.removeRouteByModule(specifier)
389401
}
390402
this.clearSSRCache(specifier)
391-
log.info('remove', specifier)
403+
log.debug('remove', specifier)
392404
}
393405
}
394406

@@ -1089,6 +1101,7 @@ export class Aleph implements IAleph {
10891101
const jsFile = join(dirname(localPath), `${name}.js`)
10901102
const cacheFp = join(this.#buildDir, jsFile)
10911103
const metaFp = cacheFp.slice(0, -3) + '.meta.json'
1104+
const isNew = !mod
10921105

10931106
let defer = (err?: Error) => { }
10941107
let source: ModuleSource | null = null
@@ -1101,8 +1114,11 @@ export class Aleph implements IAleph {
11011114
ready: new Promise((resolve) => {
11021115
defer = (err?: Error) => {
11031116
if (err) {
1104-
this.#modules.delete(specifier)
1117+
if (isNew) {
1118+
this.#modules.delete(specifier)
1119+
}
11051120
log.error(err.message)
1121+
// todo: send error to client
11061122
}
11071123
resolve()
11081124
}

server/config.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export function defaultConfig(): Readonly<RequiredConfig> {
2929
ssr: {},
3030
plugins: [],
3131
css: {
32-
cache: false,
32+
cache: true,
3333
modules: {},
3434
extract: {
3535
limit: 8 * 1024
@@ -120,10 +120,19 @@ export async function loadConfig(specifier: string): Promise<Config> {
120120
}
121121
}
122122
if (util.isPlainObject(css)) {
123-
const { extract, cache, modules, postcss } = css
123+
const { extract, cache: v, modules, postcss } = css
124+
let cache: boolean | RegExp | RegExp[] = true
125+
if (typeof v === 'boolean' || v instanceof RegExp) {
126+
cache = v
127+
} else if (Array.isArray(v)) {
128+
cache = v.filter(test => test instanceof RegExp)
129+
if (cache.length === 0) {
130+
cache = false
131+
}
132+
}
124133
config.css = {
125-
cache: Boolean(cache),
126-
extract: util.isPlainObject(extract) ? { limit: typeof extract.limit === 'number' ? extract.limit : 8 * 1024 } : Boolean(extract),
134+
cache,
135+
extract: util.isPlainObject(extract) && typeof extract.limit === 'number' ? extract : { limit: 8 * 1024 },
127136
modules: util.isPlainObject(modules) ? modules : {},
128137
postcss: isPostcssConfig(postcss) ? postcss : { plugins: ['autoprefixer'] }
129138
}

types.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,9 +188,9 @@ export type ImportMap = {
188188
*/
189189
export type CSSOptions = {
190190
/** `cache` caches remote css to local if it is true. */
191-
cache?: boolean
191+
cache?: boolean | RegExp | RegExp[]
192192
/** `extract` specifies the extract options (default is true with 8k limit). */
193-
extract?: boolean | { limit?: number }
193+
extract?: { limit?: number }
194194
/** `postcss` specifies the postcss plugins. */
195195
postcss?: { plugins: PostCSSPlugin[] }
196196
/** `modules` specifies CSS modules behavior. */

0 commit comments

Comments
 (0)