Comments (12)
I think it would make sense to not rely on squints
watch
as Vite already has the resolution built in and will only compile the files that are actually part of the module graph. But I'm not 100% sure if this might be required forresolveId
to work correctly?
Yes relying on Vite's watch would be ideal. As there are other things that Vite supports such as path aliasing. That is just an example that I am aware of.
So very similar to the steps mentioned above:
- Plugin creates an instance of a squint compiler (I am guessing that this is needed for squint to remember macros it has seen?)
resolveId
(from vite) we would return the squint compiledoutput-dir
of the file
Will spend some time with some hardcoded files to see if this works in the plugin how we expect
from vite-plugin-squint.
Cool, I'll have a look at this tomorrow
from vite-plugin-squint.
Is this the repro case @rmrt1n?
https://github.com/rmrt1n/squint-solid-demo
Original Slack conversation: https://clojurians.slack.com/archives/C03U8L2NXNC/p1712907189474459
from vite-plugin-squint.
Yes, I forgot to include it in the issue desc.
from vite-plugin-squint.
Just looked at this briefly and noticed that it seems to pick up on new classes correctly when running vite build --watch
. Not sure why that is but I guess it means this is at least in part related to hot module reloading etc.
from vite-plugin-squint.
I spent some time that day digging into it, but needed some more time to really go down the rabbit hole.
Below are my findings:
Tailwind changes are not being picked up as vite's internal postcss plugin is only checking changes for physical files. The module returns a resolveId
of .cljs.jsx
that would trick vite into thinking that there was a physical .cljs.jsx
on the filesystem but it doesn't exist. This was the approach that worked the best when integrating into the other plugins (react/solid/qwik).
Virtual Modules
I tried converting the plugin to emit virtual modules for the .cljs.jsx
files. You can see the code below:
import { compileString } from "squint-cljs";
import path, { dirname } from "path";
import fs from "fs";
export default function viteSquint(opts = {}) {
const squint = {
name: "squint_compile",
enforce: "pre",
async load(id) {
// load virtual files
if (/^\0.*.cljs.jsx$/.test(id)) {
// TODO: macros
// TODO: squint source mapping
const file = id.substring(1).replace(/.jsx$/, "");
const code = await fs.promises.readFile(file, "utf-8");
const compiled = compileString(code);
return { code: compiled, map: null };
}
},
resolveId(id, importer, options) {
// importing cljs file
if (!/\0.*/.test(id) && /\.cljs$/.test(id)) {
// make path relative if absolute
if (id.startsWith("/")) {
id = id.substring(1);
}
// if it's being imported from a virtual module, remove the \0
if (/^\0.*.cljs.jsx$/.test(importer)) {
importer = importer.substring(1);
}
const absolutePath = path.resolve(dirname(importer), id);
// return the virtual module id
return "\0" + absolutePath + ".jsx";
}
// importing another file from a module, vite doesn't resolve paths from
// virtual modules, so we need to resolve it ourselves
if (/^\0.*.cljs.jsx$/.test(importer)) {
const realPath = dirname(importer.substring(1));
const absolutePath = path.resolve(realPath, id);
// we only return if the physical file exists, otherwise it's a library import
if (fs.existsSync(absolutePath)) {
return absolutePath;
}
}
},
handleHotUpdate({file, server, modules }) {
if (/\.cljs$/.test(file)) {
// this needs to be the same id returned by resolveId this is what
// vite uses as the modules identifier
const resolveId = "\0" + file + ".jsx";
const module = server.moduleGraph.getModuleById(resolveId);
if (module) {
// invalidate dependants
server.moduleGraph.onFileChange(resolveId);
// hot reload
return [...modules, module ]
}
return modules;
}
},
};
return [squint];
}
This solved the issue with the tailwind HMR on a vanilla application. However, this approach does not work well with other plugins. For example the react plugin uses the vite/rollup createFilter. Internally this filter will never return true for virtual modules.
What next
Having interim physical files on the filesystem will be a lot easier than trying to do any tricks. We can go down two paths doing this, I would love input on which direction may be best:
- Start squints CLI in the background in the plugin to do the conversions (see thread). We can make it output to a particular directory and we can use the
resolveId
to resolve where vite should be checking for the files. - We use the existing plugin and just persist the
.cljs.jsx
files to disk.
For option 1 this may be easier/better for supporting macros and a REPL, as we are resolving files we could also resolve external future sourcemaps with this approach.
For option 2 we will still have to solve macros and how a potential future REPL would work.
from vite-plugin-squint.
from vite-plugin-squint.
Option 1 sounds good to me as well. I'm wondering could the generated files be anywhere on the filesystem? Ideally I think the plugin shouldn't generate files into the source tree or something like that, maybe it should be just a tmp dir even.
I think avoiding the shell out is good as otherwise that will produce quite a bit of overhead I guess.
Just to recap what this would mean (this might be wrong)
- Vite sees a cljs file
- It uses a CLI-like API to compile the file into a specific directory
- It extends
resolveId
to resolve to that file on disk
PROFIT?! Am I getting this right?
I think it would make sense to not rely on squints watch
as Vite already has the resolution built in and will only compile the files that are actually part of the module graph. But I'm not 100% sure if this might be required for resolveId
to work correclty?
from vite-plugin-squint.
Ideally I think the plugin shouldn't generate files into the source tree or something like that, maybe it should be just a tmp dir even.
squint.edn
has :output-dir
for this (and defaults to the same directory as the .cljs
source)
It uses a CLI-like API to compile the file into a specific directory
Yes, the function squint.internal.cli/compile-file
should be exposed for this (and made suitable for JS consumption)
The :output-dir
could be set here too, e.g. to a /tmp
directory. Would be nice if this could be made configurable so I can use this for libraries that need outputted JS into a dist
directory as well.
resolveId
I'm not sure what resolveId is about
from vite-plugin-squint.
If resolveId
is a function that sees an input .cljs
file and should return a path into the :output-dir
that could be exposed as well.
from vite-plugin-squint.
Cool, thanks for the experiment. Hardcoded experiment would be good, then I won't have to prematurely expose an API :)
from vite-plugin-squint.
I have created a POC that we can start working with #20
The way it works:
- When vite calls
options
we could create an instance of the compiler - When
resolveId
is called we do the following:
i. If there is no.cljs
extension in the import, we check if acljs
file exists with that extension and resolve it.
ii. for.cljs
files we return the path to theoutput-dir/filename.jsx
. For examplesrc/main.cljs
will besquint_out/main.jsx
iii. for imports that are files. Such as images, CSS, or json files. We return the absolute path as thejsx
file in the output directory breaks relative paths. This is breaks in the current squint react example - When
load
is called. We only compile if the file has changed to prevent an infinite loop and return thejsx
from the compiled output - When
handleHotUpdate
is called we invalidate using theresolveId
that we returned before.
This works for both HMR and Tailwind HMR. This uses vites watch file logic, which helps us support path aliasing and json imports (and everything else that may be vite specific)
This does not work for macros, vite will also not ever call resolveId
as it's not imported. Thus vite will never watch this file for changes. Perhaps the compiler could start in a watch mode only for macros?
from vite-plugin-squint.
Related Issues (8)
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. πππ
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google β€οΈ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from vite-plugin-squint.