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

Commit 7636b14

Browse files
committed
breaking: improve loader plugin
1 parent feb4e16 commit 7636b14

File tree

8 files changed

+124
-70
lines changed

8 files changed

+124
-70
lines changed

compiler/mod.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ export async function transform(url: string, code: string, options: TransformOpt
123123
}
124124

125125
const { loaders, ...transformOptions } = options
126+
126127
let {
127128
code: jsContent,
128129
deps,
@@ -145,7 +146,7 @@ export async function transform(url: string, code: string, options: TransformOpt
145146
if (loaders !== undefined) {
146147
if (style.type !== 'css') {
147148
for (const loader of loaders) {
148-
if (loader.test.test(`.${style.type}`)) {
149+
if (loader.test.test(`.${style.type}`) && loader.transform) {
149150
const { code, type } = await loader.transform({ url: key, content: (new TextEncoder).encode(tpl) })
150151
if (type === 'css') {
151152
tpl = code
@@ -155,7 +156,7 @@ export async function transform(url: string, code: string, options: TransformOpt
155156
}
156157
}
157158
for (const loader of loaders) {
158-
if (loader.test.test('.css')) {
159+
if (loader.test.test('.css') && loader.transform) {
159160
const { code, type } = await loader.transform({ url: key, content: (new TextEncoder).encode(tpl) })
160161
if (type === 'css') {
161162
tpl = code

plugins/css.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Plugin, PluginCreator } from 'https://esm.sh/[email protected]'
1+
import { Plugin, PluginCreator } from 'https://esm.sh/[email protected]'
22
import { join } from 'https://deno.land/[email protected]/path/mod.ts'
33
import { existsFileSync } from '../shared/fs.ts'
44
import util from '../shared/util.ts'
@@ -17,6 +17,8 @@ export type Options = {
1717
}
1818

1919
export default (options?: Options): LoaderPlugin => {
20+
const encoder = new TextEncoder()
21+
2022
let pcssProcessor: any = null
2123
let cleanCSS: any = null
2224
let isProd: any = null
@@ -47,11 +49,11 @@ export default (options?: Options): LoaderPlugin => {
4749
}
4850
}
4951
return {
50-
code: [
52+
code: encoder.encode([
5153
'import { applyCSS } from "https://deno.land/x/aleph/framework/core/style.ts"',
5254
`applyCSS(${JSON.stringify(url)}, ${JSON.stringify(css)})`
53-
].join('\n'),
54-
map: undefined // todo: generate map
55+
].join('\n'))
56+
// todo: generate map
5557
}
5658
}
5759
}

plugins/sass.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
import type { Options, Result } from 'https://esm.sh/[email protected]'
1+
import { Options, Result } from 'https://esm.sh/[email protected]'
22
import type { LoaderPlugin } from '../types.ts'
33

44
type Sass = { renderSync(options: Options): Result }
55

66
export default (opts?: Options): LoaderPlugin => {
7+
const decoder = new TextDecoder()
8+
79
let sass: Sass | null = null
810

911
return {
@@ -22,13 +24,13 @@ export default (opts?: Options): LoaderPlugin => {
2224
indentedSyntax: url.endsWith('.sass'),
2325
...opts,
2426
file: url,
25-
data: (new TextDecoder).decode(content),
27+
data: decoder.decode(content),
2628
sourceMap: true
2729
})
2830
return {
29-
code: (new TextDecoder).decode(css),
31+
code: css,
3032
type: 'css',
31-
map: map ? (new TextDecoder).decode(map) : undefined,
33+
map: map,
3234
}
3335
}
3436
}

plugins/wasm.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@ export default (): LoaderPlugin => ({
44
name: 'wasm-loader',
55
type: 'loader',
66
test: /\.wasm$/i,
7-
transform: ({ content }) => ({
8-
code: `
9-
const wasmBytes = new Uint8Array([${content.join(',')}])
10-
const wasmModule = new WebAssembly.Module(wasmBytes)
11-
const { exports } = new WebAssembly.Instance(wasmModule)
12-
export default exports
13-
`
14-
})
7+
transform: ({ content }) => {
8+
const encoder = new TextEncoder()
9+
return {
10+
code: encoder.encode([
11+
`const wasmBytes = new Uint8Array([${content.join(',')}])`,
12+
'const wasmModule = new WebAssembly.Module(wasmBytes)',
13+
'const { exports } = new WebAssembly.Instance(wasmModule)',
14+
'export default exports',
15+
].join('\n'))
16+
}
17+
}
1518
})

server/app.ts

Lines changed: 84 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import type {
3232
Config,
3333
DependencyDescriptor,
3434
LoaderPlugin,
35-
LoaderTransformResult,
35+
LoaderTransformOutput,
3636
Module,
3737
RouterURL,
3838
ServerApplication,
@@ -221,7 +221,7 @@ export class Application implements ServerApplication {
221221
}
222222
if (validated) {
223223
await this.compile(url)
224-
this.#pageRouting.update(this.createRouteModule(url))
224+
this.#pageRouting.update(...this.createRouteUpdate(url))
225225
}
226226
}
227227
}
@@ -232,7 +232,7 @@ export class Application implements ServerApplication {
232232
for await (const { path: p } of walk(apiDir, { ...walkOptions, exts: moduleExts })) {
233233
const url = util.cleanPath('/api/' + util.trimPrefix(p, apiDir))
234234
await this.compile(url)
235-
this.#apiRouting.update(this.createRouteModule(url))
235+
this.#apiRouting.update(...this.createRouteUpdate(url))
236236
}
237237
}
238238

@@ -274,17 +274,39 @@ export class Application implements ServerApplication {
274274
if (trimModuleExt(url) === '/app') {
275275
this.#renderer.clearCache()
276276
} else if (url.startsWith('/pages/')) {
277-
this.#renderer.clearCache(url)
278-
this.#pageRouting.update(this.createRouteModule(url))
277+
this.#renderer.clearCache(toPagePath(url))
278+
this.#pageRouting.update(...this.createRouteUpdate(url))
279279
} else if (url.startsWith('/api/')) {
280-
this.#apiRouting.update(this.createRouteModule(url))
280+
this.#apiRouting.update(...this.createRouteUpdate(url))
281281
}
282282
}
283283
if (hmrable) {
284+
let pagePath: string | undefined = undefined
285+
let useDeno: boolean | undefined = undefined
286+
let isIndexModule: boolean | undefined = undefined
287+
if (mod.url.startsWith('/pages/')) {
288+
const [path, _, options] = this.createRouteUpdate(mod.url)
289+
pagePath = path
290+
useDeno = options.useDeno
291+
isIndexModule = options.isIndexModule
292+
} else {
293+
if (['/app', '/404'].includes(trimModuleExt(mod.url))) {
294+
this.lookupDeps(mod.url, dep => {
295+
if (dep.url.startsWith('#useDeno-')) {
296+
useDeno = true
297+
return false
298+
}
299+
})
300+
}
301+
}
284302
if (type === 'add') {
285-
this.#fsWatchListeners.forEach(e => e.emit('add', { url: mod.url }))
303+
this.#fsWatchListeners.forEach(e => {
304+
e.emit('add', { url: mod.url, pagePath, isIndexModule, useDeno })
305+
})
286306
} else {
287-
this.#fsWatchListeners.forEach(e => e.emit('modify-' + mod.url))
307+
this.#fsWatchListeners.forEach(e => {
308+
e.emit('modify-' + mod.url, { useDeno })
309+
})
288310
}
289311
}
290312
update(mod)
@@ -396,12 +418,12 @@ export class Application implements ServerApplication {
396418
}
397419

398420
/** add a new page module by given path and source code. */
399-
async addModule(url: string, options: { code?: string, once?: boolean } = {}): Promise<Module> {
421+
async addModule(url: string, options: { code?: string } = {}): Promise<Module> {
400422
const mod = await this.compile(url, { sourceCode: options.code })
401423
if (url.startsWith('/pages/')) {
402-
this.#pageRouting.update(this.createRouteModule(url))
424+
this.#pageRouting.update(...this.createRouteUpdate(url))
403425
} else if (url.startsWith('/api/')) {
404-
this.#apiRouting.update(this.createRouteModule(url))
426+
this.#apiRouting.update(...this.createRouteUpdate(url))
405427
}
406428
return mod
407429
}
@@ -536,14 +558,19 @@ export class Application implements ServerApplication {
536558
routes: this.#pageRouting.routes,
537559
rewrites: this.config.rewrites,
538560
sharedModules: Array.from(this.#modules.values()).filter(({ url }) => {
539-
switch (trimModuleExt(url)) {
540-
case '/404':
541-
case '/app':
542-
return true
543-
default:
544-
return false
561+
return ['/app', '/404'].includes(trimModuleExt(url))
562+
}).map(({ url }) => {
563+
let useDeno: boolean | undefined = undefined
564+
if (this.config.ssr !== false) {
565+
this.lookupDeps(url, dep => {
566+
if (dep.url.startsWith('#useDeno-')) {
567+
useDeno = true
568+
return false
569+
}
570+
})
545571
}
546-
}).map(({ url }) => this.createRouteModule(url)),
572+
return { url, useDeno }
573+
}),
547574
renderMode: this.config.ssr ? 'ssr' : 'spa'
548575
}
549576

@@ -686,8 +713,10 @@ export class Application implements ServerApplication {
686713
return dir
687714
}
688715

689-
private createRouteModule(url: string): RouteModule {
690-
let useDeno: true | undefined = undefined
716+
private createRouteUpdate(url: string): [string, string, { isIndexModule?: boolean, useDeno?: boolean }] {
717+
let pathPath = toPagePath(url)
718+
let useDeno: boolean | undefined = undefined
719+
let isIndexModule: boolean | undefined = undefined
691720
if (this.config.ssr !== false) {
692721
this.lookupDeps(url, dep => {
693722
if (dep.url.startsWith('#useDeno-')) {
@@ -696,14 +725,30 @@ export class Application implements ServerApplication {
696725
}
697726
})
698727
}
699-
return { url, useDeno }
728+
if (pathPath !== '/') {
729+
for (const ext of moduleExts) {
730+
if (url.endsWith('/index.' + ext)) {
731+
isIndexModule = true
732+
break
733+
}
734+
}
735+
}
736+
return [pathPath, url, { isIndexModule, useDeno }]
700737
}
701738

702739
/** apply loaders recurively. */
703740
private async applyLoader(
704741
loader: LoaderPlugin,
705742
input: { url: string, content: Uint8Array, map?: Uint8Array }
706-
): Promise<Omit<LoaderTransformResult, 'loader'>> {
743+
): Promise<LoaderTransformOutput> {
744+
if (!loader.transform) {
745+
const decoder = new TextDecoder()
746+
return {
747+
code: decoder.decode(input.content),
748+
map: input.map ? decoder.decode(input.map) : undefined
749+
}
750+
}
751+
707752
const { code, map, type } = await loader.transform(input)
708753
if (type) {
709754
for (const plugin of this.config.plugins) {
@@ -724,12 +769,12 @@ export class Application implements ServerApplication {
724769
private async fetchModule(url: string): Promise<{ content: Uint8Array, contentType: string | null }> {
725770
for (const plugin of this.config.plugins) {
726771
if (plugin.type === 'loader' && plugin.test.test(url) && plugin.resolve !== undefined) {
727-
const ret = plugin.resolve(url)
772+
const v = plugin.resolve(url)
728773
let content: Uint8Array
729-
if (ret instanceof Promise) {
730-
content = (await ret).content
774+
if (v instanceof Promise) {
775+
content = (await v)
731776
} else {
732-
content = ret.content
777+
content = v
733778
}
734779
if (content instanceof Uint8Array) {
735780
return { content, contentType: null }
@@ -824,9 +869,9 @@ export class Application implements ServerApplication {
824869
url: string,
825870
sourceContent: Uint8Array,
826871
contentType: string | null
827-
): Promise<[string, SourceType] | null> {
872+
): Promise<{ code: string, type: SourceType } | null> {
828873
let sourceCode = (new TextDecoder).decode(sourceContent)
829-
let sourceType: SourceType = SourceType.Unknown
874+
let sourceType: SourceType | null = null
830875

831876
if (contentType !== null) {
832877
switch (contentType.split(';')[0].trim()) {
@@ -866,12 +911,15 @@ export class Application implements ServerApplication {
866911
case 'tsx':
867912
sourceType = SourceType.TSX
868913
break
914+
default:
915+
sourceType = SourceType.Unknown
916+
break
869917
}
870918
break
871919
}
872920
}
873921

874-
if (sourceType === SourceType.Unknown) {
922+
if (sourceType === null) {
875923
switch (extname(url).slice(1).toLowerCase()) {
876924
case 'mjs':
877925
case 'js':
@@ -891,7 +939,7 @@ export class Application implements ServerApplication {
891939
}
892940
}
893941

894-
return [sourceCode, sourceType]
942+
return { code: sourceCode, type: sourceType }
895943
}
896944

897945
/** compile a moudle by given url, then cache on the disk. */
@@ -925,7 +973,7 @@ export class Application implements ServerApplication {
925973
deps: [],
926974
sourceHash: '',
927975
hash: '',
928-
jsFile: '',
976+
jsFile: util.cleanPath(`${saveDir}/${name}.js`),
929977
}
930978
if (!once) {
931979
this.#modules.set(url, mod)
@@ -993,12 +1041,12 @@ export class Application implements ServerApplication {
9931041
}
9941042

9951043
const t = performance.now()
996-
const [sourceCode, sourceType] = source
997-
const { code, deps, starExports, map } = await transform(url, sourceCode, {
1044+
1045+
const { code, deps, starExports, map } = await transform(url, source.code, {
9981046
...this.defaultCompileOptions,
9991047
swcOptions: {
10001048
target: 'es2020',
1001-
sourceType
1049+
sourceType: source.type
10021050
},
10031051
// workaround for https://github.com/denoland/deno/issues/9849
10041052
resolveStarExports: !this.isDev && Deno.version.deno.replace(/\.\d+$/, '') === '1.8',
@@ -1016,8 +1064,8 @@ export class Application implements ServerApplication {
10161064
if (starExports && starExports.length > 0) {
10171065
for (let index = 0; index < starExports.length; index++) {
10181066
const url = starExports[index]
1019-
const [sourceCode, sourceType] = await this.resolveModule(url)
1020-
const names = await parseExportNames(url, sourceCode, { sourceType })
1067+
const source = await this.resolveModule(url)
1068+
const names = await parseExportNames(url, source.code, { sourceType: source.type })
10211069
jsContent = jsContent.replace(`export * from "${url}:`, `export {${names.filter(name => name !== 'default').join(',')}} from "`)
10221070
}
10231071
}
@@ -1036,8 +1084,6 @@ export class Application implements ServerApplication {
10361084
log.debug(`compile '${url}' in ${Math.round(performance.now() - t)}ms`)
10371085
}
10381086

1039-
mod.jsFile = util.cleanPath(`${saveDir}/${name}.js`)
1040-
10411087
// compile deps
10421088
for (const dep of mod.deps) {
10431089
if (!dep.url.startsWith('#')) {

0 commit comments

Comments
 (0)