Race condition between TypeScript and Rollup
Closed this issue · 8 comments
Issue
There is a race condition on first run. App.svelte
cannot be resolved on first run of the dev
script, yet can be resolved on the second run (or upon saving App.tsx
). I guess this is because main.js
refers to App.Svelte
, yet TypeScript hasn't transpiled App.tsx
to App.svelte
yet.
Installation
git clone https://github.com/mistlog/svelte-draft-template.git
cd svelte-draft-template
npm install
Running the dev
script for the first time ❌
npm run dev
> svelte-app@1.0.0 dev /Users/jwork/Documents/git/svelte-draft-template
> concurrently "npm run transcribe:watch" "rollup -c -w"
[0]
[0] > svelte-app@1.0.0 transcribe:watch /Users/jwork/Documents/git/svelte-draft-template
[0] > sd ./src -w
[0]
[1] rollup v1.30.1
[1] bundles src/main.js → public/build/bundle.js...
[1] [!] Error: Could not resolve './App.svelte' from src/main.js
[1] Error: Could not resolve './App.svelte' from src/main.js
[1] at error (/Users/jwork/Documents/git/svelte-draft-template/node_modules/rollup/dist/shared/node-entry.js:5400:30)
[1] at ModuleLoader.handleResolveId (/Users/jwork/Documents/git/svelte-draft-template/node_modules/rollup/dist/shared/node-entry.js:12389:24)
[1] at ModuleLoader.<anonymous> (/Users/jwork/Documents/git/svelte-draft-template/node_modules/rollup/dist/shared/node-entry.js:12277:30)
[1] at Generator.next (<anonymous>)
[1] at fulfilled (/Users/jwork/Documents/git/svelte-draft-template/node_modules/rollup/dist/shared/node-entry.js:38:28)
[1]
Running the transcribe:watch
script subsequently ✅
npm run transcribe:watch
> svelte-app@1.0.0 transcribe:watch /Users/jwork/Documents/git/svelte-draft-template
> sd ./src -w
Running the rollup -c -w
script subsequently ✅
node_modules/.bin/rollup -c -w
Your application is ready~! 🚀
- Local: http://localhost:5000
────────────────── LOGS ──────────────────
[15:46:23] 200 ─ 4.33ms ─ /
[15:46:23] 200 ─ 1.63ms ─ /global.css
[15:46:23] 200 ─ 1.44ms ─ /build/bundle.css
[15:46:23] 200 ─ 1.78ms ─ /build/bundle.js
[15:46:23] 200 ─ 14.93ms ─ /favicon.png
Running the dev
script subsequently once more ✅
npm run dev
Your application is ready~! 🚀
- Local: http://localhost:5000
────────────────── LOGS ──────────────────
[15:46:23] 200 ─ 4.33ms ─ /
[15:46:23] 200 ─ 1.63ms ─ /global.css
[15:46:23] 200 ─ 1.44ms ─ /build/bundle.css
[15:46:23] 200 ─ 1.78ms ─ /build/bundle.js
[15:46:23] 200 ─ 14.93ms ─ /favicon.png
Solution
In Webpack, in each rule, it is possible to provide an array of loaders to define an ordered pipeline by which to process files during the bundling process. It is quite common to process from SCSS -> CSS before bundling, for example. I'm sure Rollup will have an equivalent API allowing the TypeScript Compiler (or Babel) to be used to watch .tsx
files and process them to .svelte
files (ideally without emitting them to disk) without having to deal with a race condition between rollup and TypeScript. What do you think about this approach?
Thanks for pointing this out and I'm sorry that I didn't notice this problem.
I think this approach is great and it looks more professional to avoid intermediate files. After some search, it seems that rollup-plugin-svelte provides preprocess customization, I'm not familiar with bundling, so I don't know which is better(write a loader or plugin from scratch or use rollup-plugin-svelte). Do you have interest in solving this problem? Or any suggestions on that so that we can solve it with best practice.
I'm not familiar with bundling
I'm not greatly familiar myself – and I don't know a thing about Rollup, unfortunately! I'm more familiar with Webpack (but even then, I don't know anything about writing loaders and plugins).
Do you have interest in solving this problem?
I don't think I have enough time, regrettably! But I am interested in the project and could certainly advise.
For Webpack
It looks like there's a Webpack template, which makes use of a Webpack loader, svelte-loader
.
If it were Webpack, I think it would involve setting up a rule like this:
- Test for
/\.tsx$/
. - Convert TSX to Svelte. I see you've made
svelte-draft
for this. It would need to be made into a Webpack loader (somehow – I have no experience here either). Or there might be a way to do it as a babel plugin in combination with awesome-typescript-loader (as I do here to convert TSX to JS). - Then convert Svelte to JS via
svelte-loader
.
For Rollup
If it were Rollup, I'm sure the process is similar. I guess:
- Test for
/\.tsx$/
. - Convert TSX to Svelte via turning
svelte-draft
into a Rollup plugin (if "plugin" is the right word), if it's not already. - Then convert Svelte to JS via
rollup-plugin-svelte
.
I'm not familiar with bundling, so I don't know which is better (write a loader or plugin from scratch or use
rollup-plugin-svelte
).
I think rollup-plugin-svelte
would still be used as-is, but that would be a downstream step. The remaining task is to make svelte-draft
do its logic as a rollup plugin to feed into it.
Thanks for your detailed reply!
I will try to solve it as soon as possible!
I've thought of a starting point for a proof-of-concept.
Add @rollup/plugin-typescript
into your rollup config by adding typescript()
into your plugins
array (e.g. as demonstrated here). If I'm understanding the order of plugin execution correctly, you'd place it before rollup-plugin-svelte
in the array of plugins. Now any .ts
, .d.ts
, and .tsx
files (which are the default include
options) will be resolved by Rollup and fed into the TypeScript plugin.
Next, create a transformer plugin like this:
import { createFilter } from 'rollup-pluginutils';
export default function myPlugin ( options = {} ) {
const filter = createFilter( options.include, options.exclude );
return {
transform ( code, id ) {
if ( !filter( id ) ) return;
// proceed with the transformation...
return {
code: generatedCode,
map: generatedSourceMap
};
}
};
}
createFilter()
is documented here; you'll have to find some way to sub-filter for .tsx
files within the list of included files (as a later step, you can think about .jsx
files too, but let's focus on this pipeline first).
The transformation should then perform whatever svelte-draft
is doing at present. Note that rollup
's plugin conventions state that methods should be asynchronous where possible, though you can start with sync if it's easier for a proof-of-concept
Here is what @rollup/plugin-typescript
's own transformer function looks like, if it's any help:
I'm not sure how easy it will be to provide source maps, but they can possibly be left undefined
for a proof-of-concept.
Unsolved question: Although this approach achieves the transform from TSX -> Svelte, I'm not sure how you'd get the Svelte plugin to resolve this module as it's only resolving .svelte
files. Maybe if you told the Svelte plugin to include .tsx
files, it would succesfully take in the transformed .tsx
because it's after the TypeScript plugin in the execution order?
Hope this helps..!
Thanks for the material you provided, it's definitely helpful! It's a concrete starting point and I will have a try.
For unsolved question, it seems that rollup-plugin-svelte receives options extensions
, maybe we can specify .tsx and the plugin will treat it as .svelte file and compile.
I will sync my progress latter.
The pipeline is clear now. Add extensions field in rollup.config.js as follows:
svelte({
...
extensions: ["./src/**/*.tsx"],
...
}),
Then svelte will find file ends with .tsx and compile it just as .svelte.
Then svelte will find file ends with .tsx and compile it just as .svelte.
The next question is: If you place rollup-plugin-svelte-draft
(tentative name) before rollup-plugin-svelte
in the plugins
array, and add ./src/**/*.tsx
as a specified extension to rollup-plugin-svelte
, does that mean that all .tsx
files resolved by Rollup will have been pre-processed by rollup-plugin-svelte-draft
before passing onto rollup-plugin-svelte
?
does that mean that all
.tsx
files resolved by Rollup will have been pre-processed byrollup-plugin-svelte-draft
before passing ontorollup-plugin-svelte
?
Yes, the order of plugins is exactly the order files get processed.
I'm sorry that I made a mistake:
svelte({
...
extensions: ["./src/**/*.tsx"],
...
}),
the extensions should be [".tsx"]
and after we add rollup-plugin-svelte-draft
, it would be:
import SvelteDraft from "rollup-plugin-svelte-draft";
...
plugins: [
SvelteDraft({include:["./src/**/*.tsx"]}),
svelte({
extensions: [".tsx"]
...
}),
I'm working on such a plugin. hope I can finish it today! I will link repo of it later.
Done! Thanks for your help again!
The repo of plugin is: https://github.com/mistlog/rollup-plugin-svelte-draft.