8
8
import { ensure } from "@core/unknownutil" ;
9
9
import { toFileUrl } from "@std/path/to-file-url" ;
10
10
import { fromFileUrl } from "@std/path/from-file-url" ;
11
- import { join } from "@std/path/join" ;
12
- import { dirname } from "@std/path/dirname" ;
13
11
import { parse as parseJsonc } from "@std/jsonc" ;
14
12
15
13
type PluginModule = {
@@ -21,14 +19,18 @@ export class Plugin {
21
19
#loadedWaiter: Promise < void > ;
22
20
#unloadedWaiter?: Promise < void > ;
23
21
#disposable: AsyncDisposable = voidAsyncDisposable ;
22
+ #scriptUrl: URL ;
24
23
25
24
readonly name : string ;
26
- readonly script : string ;
25
+
26
+ get script ( ) : string {
27
+ return this . #scriptUrl. href ;
28
+ }
27
29
28
30
constructor ( denops : Denops , name : string , script : string ) {
29
31
this . #denops = denops ;
30
32
this . name = name ;
31
- this . script = resolveScriptUrl ( script ) ;
33
+ this . #scriptUrl = resolveScriptUrl ( script ) ;
32
34
this . #loadedWaiter = this . #load( ) ;
33
35
}
34
36
@@ -39,7 +41,7 @@ export class Plugin {
39
41
async #load( ) : Promise < void > {
40
42
await emit ( this . #denops, `DenopsSystemPluginPre:${ this . name } ` ) ;
41
43
try {
42
- const mod : PluginModule = await importPlugin ( this . script ) ;
44
+ const mod : PluginModule = await importPlugin ( this . #scriptUrl ) ;
43
45
this . #disposable = await mod . main ( this . #denops) ?? voidAsyncDisposable ;
44
46
} catch ( e ) {
45
47
// Show a warning message when Deno module cache issue is detected
@@ -110,12 +112,16 @@ const voidAsyncDisposable = {
110
112
111
113
const loadedScripts = new Set < string > ( ) ;
112
114
113
- function createScriptSuffix ( script : string ) : string {
115
+ function refreshScriptFragment ( scriptUrl : URL ) : URL {
114
116
// Import module with fragment so that reload works properly
115
117
// https://github.com/vim-denops/denops.vim/issues/227
116
- const suffix = loadedScripts . has ( script ) ? `#${ performance . now ( ) } ` : "" ;
117
- loadedScripts . add ( script ) ;
118
- return suffix ;
118
+ if ( loadedScripts . has ( scriptUrl . href ) ) {
119
+ // Keep the original fragment and add a timestamp
120
+ const fragment = `${ scriptUrl . hash } #${ performance . now ( ) } ` ;
121
+ return new URL ( fragment , scriptUrl ) ;
122
+ }
123
+ loadedScripts . add ( scriptUrl . href ) ;
124
+ return scriptUrl ;
119
125
}
120
126
121
127
/** NOTE: `emit()` is never throws or rejects. */
@@ -127,11 +133,11 @@ async function emit(denops: Denops, name: string): Promise<void> {
127
133
}
128
134
}
129
135
130
- function resolveScriptUrl ( script : string ) : string {
136
+ function resolveScriptUrl ( script : string ) : URL {
131
137
try {
132
- return toFileUrl ( script ) . href ;
138
+ return toFileUrl ( script ) ;
133
139
} catch {
134
- return new URL ( script , import . meta . url ) . href ;
140
+ return new URL ( script ) ;
135
141
}
136
142
}
137
143
@@ -148,9 +154,9 @@ function isDenoCacheIssueError(e: unknown): boolean {
148
154
}
149
155
150
156
async function tryLoadImportMap (
151
- script : string ,
157
+ scriptUrl : URL ,
152
158
) : Promise < ImportMap | undefined > {
153
- if ( script . startsWith ( "http://" ) || script . startsWith ( "https://" ) ) {
159
+ if ( scriptUrl . protocol !== "file:" ) {
154
160
// We cannot load import maps for remote scripts
155
161
return undefined ;
156
162
}
@@ -160,13 +166,9 @@ async function tryLoadImportMap(
160
166
"import_map.json" ,
161
167
"import_map.jsonc" ,
162
168
] ;
163
- // Convert file URL to path for file operations
164
- const scriptPath = script . startsWith ( "file://" )
165
- ? fromFileUrl ( new URL ( script ) )
166
- : script ;
167
- const parentDir = dirname ( scriptPath ) ;
168
169
for ( const pattern of PATTERNS ) {
169
- const importMapPath = join ( parentDir , pattern ) ;
170
+ const importMapUrl = new URL ( pattern , scriptUrl ) ;
171
+ const importMapPath = fromFileUrl ( importMapUrl ) ;
170
172
try {
171
173
return await loadImportMap ( importMapPath , {
172
174
loader : ( path : string ) => {
@@ -185,13 +187,13 @@ async function tryLoadImportMap(
185
187
return undefined ;
186
188
}
187
189
188
- async function importPlugin ( script : string ) : Promise < PluginModule > {
189
- const suffix = createScriptSuffix ( script ) ;
190
- const importMap = await tryLoadImportMap ( script ) ;
190
+ async function importPlugin ( scriptUrl : URL ) : Promise < PluginModule > {
191
+ scriptUrl = refreshScriptFragment ( scriptUrl ) ;
192
+ const importMap = await tryLoadImportMap ( scriptUrl ) ;
191
193
if ( importMap ) {
192
194
const importer = new ImportMapImporter ( importMap ) ;
193
- return await importer . import < PluginModule > ( ` ${ script } ${ suffix } ` ) ;
195
+ return await importer . import < PluginModule > ( scriptUrl . href ) ;
194
196
} else {
195
- return await import ( ` ${ script } ${ suffix } ` ) ;
197
+ return await import ( scriptUrl . href ) ;
196
198
}
197
199
}
0 commit comments