Saturday 27 May 2023

Svelte further than ever before

Bit Rubbish

The sourcemaps built here are broken, but we cover several other things too!

Then I discovered...

Blogger isn't safe for writing.
You must hit update to save your writing! (Sometimes? The cloud icon says)
So here's a sharp interlude.




Out In The Con Tree

-Con bundle various aspects of a thing:
 -Cont its label (eg "A Dublin City" in the following video)
 ...others here, eg -Dir for a directory listing
 -Conz wrap descendant -Con (the listing of stuff inside a thing)

They build trees of -Con/**/-Con, aka -Con//-Con.
It is used as a format for data dumping - pulling information out of Con.c.s=data.
I_mind.In uses it for instantiating a flock of atoms C:Insphere**
    and processing whatever those atoms bring.

We give it a directory to open first and it adds jobs onto it as we click to open more things.

In -Con/-Dir, Con.c.s is the s**-part instruction that lead to a -Dir being made in the first place. The s** all connect up, are one per Con. Ie:
Dir.svelte:
// we are in a -Con(s)/-Dir:C
export let C
let Con = C.y.up
let s = Con.c.s
if (!s.c.pi == 'Dir') throw "!Dir"
// climb the s**? find all -Dir above us we are relative to
// to formlink to the whole thing
let path = o_up(s).filter(s => !s.c.rootdir && s.c.pi == 'Dir').reverse()
let dir = path.map(s => s.t).join("/")

That first line has some io notation|expression (without the i|o):

The -Con(s) expression means the s-sphere (s**) is joined along this -Con-sphere.
-Dir:C means named C (the key: notation right-handed).
The other things have implied names (Con, s).

We then climb the s-sphere (aka s^^^) pulling out all these things. There is a root -Dir to omit, whose .t is just '/' and makes dir='' for itself here.

More instructions for further selections|operations can be added to the s** pile.

For example:

Not reactive enough. We click this:
Dir.svelte:
{#if f.d}<a on:click={() => nestDir(f)} class='large'>{f.f}</a>
to, now with dispatch:
function nestDir(f) {
pit(s,f.f,'-Dir')
dispatch('reCon')
}
up to Con.svelte:
<svelte:component on:reCon="{reCon}" this={pis[n.c.pi]} C={n}/>
to:
// refreshing the process, when children want to adjust things
function reCon (e, negate=false) {
C = reConstruct(C)
}
Which should skip having to ring() up your compute manually:

The flickering is from insisting on re-fetching data:
Dir.svelte:
function upto() {
req ||= fetchData()
}

And now is smooth:



And so:
// N[f+] come without src, since it is long
let fsrc = (N) => {
N.map(f => f.src = formlink('thu',dir,f.f)+'.webp')
N.map(f => f.full_src = formlink('dir',dir,f.f))
}
Lets serve the original (the /dir/ api endpoint serves files) when it gets big:
<img on:click={(e)=>animg(e,f)}
srcset={`${new URL(f.src)} 800w, ${new URL(f.full_src)} 2100w`}
alt="pretty"/>
This doesn't know the real size of <img> on the page with all its layout.
The 800w refers to the maximum size of the viewport!
Again, not about the size of the <img> on the page.
So if your viewport=800w, <img> covers 1/4, then use a 200w source (specifying 800w in srcset).
Check it out:


Will not do for thousands of pictures wandering and zooming around.
Better go model some more stuff and change src ourselves.

to compile a javascript variant

ChatGPT (which was not too correct throughout this grind) tells me vite.config.ts is apparently where to put language transformers...

Here's what I want to achieve:

// # comment to // comment
// $C->{c}->{s} =~ s/^(\s*)#/$1\/\//gsm;
// $C->{c}->{s} =~ s/( \{|}|;) #/$1 \/\//gsm;
let commenty = /^(\s*)#/gm
let match
while (match = commenty.exec(source)) {
let whole = match[0]
let indent = match[1]
let start = match.index;
let end = start + whole.length;
s.overwrite(start, end, indent+'//');
}

let blatant = /blatant/gm
while (match = blatant.exec(source)) {
let whole = match[0]
let start = match.index;
let end = start + whole.length;
s.overwrite(start, end, 'blonitn');
}


Code expecting '# comments' to work is causing a fuss before our transform happens:
src/routes/Code.svelte:98:5 - error TS1127: Invalid character.

98     # yes
       ~
src/routes/Code.svelte:98:7 - error TS1005: ';' expected.

98     # yes
         ~~~


This plugin, randomly photographed not transmitting a source map...

export function compilePlugin() {
console.log("Loaded stylehouse_lite")
return {
name: 'stylehouse_lite-compile-plugin',
transform(code:string, id:string) {
console.log("Have a "+id)
if (id.endsWith('.ts') || id.endsWith('.svelte')) {
console.log("Found a "+id)

let compiled = stylehouse_lite(code);
if (code != compiled.code) console.log("We won!!!",compiled.code)
return code != compiled.code ? compiled.code : null
let not = {
code: compiled.code,
map: compiled.map
};
}
}
}
}

letz.git 0d8554dc8fd58
...when the offending matter ('# comment') is removed from Code.svelte, "We won" shows 'blatant' compiling!
And also, before we got to this transform, something put an assortment of symbols and connections at the end:
const click_handler = () => bleep();
const click_handler_1 = () => bloop();
const click_handler_2 = () => reconver();

$$self.$capture_state = () => ({
onMount,
page,
goto,
sto,
Le,
St_main,
St_loop,
ex,
Construct,
sip_dispatch,
Diring,
Con,
stylehouse_lite,
grammar,
buildParser,
parser,
b,
a,
para,
sipd,
moment,
laCon,
con,
reset_tocon,
tocon,
dat,
refresh,
bleep,
mind,
bloop,
conver,
reconver,
keys,
handleKeydown,
compiler,
compiled,
flub,
Codemirror,
Lezing,
look,
dobla,
junk,
$page,
$sto
});

$$self.$inject_state = $$props => {
if ('parser' in $$props) $$invalidate(5, parser = $$props.parser);
if ('b' in $$props) $$invalidate(6, b = $$props.b);
if ('a' in $$props) a = $$props.a;
if ('sipd' in $$props) $$invalidate(23, sipd = $$props.sipd);
if ('moment' in $$props) $$invalidate(13, moment = $$props.moment);
if ('laCon' in $$props) laCon = $$props.laCon;
if ('con' in $$props) $$invalidate(0, con = $$props.con);
if ('dat' in $$props) dat = $$props.dat;
if ('refresh' in $$props) $$invalidate(1, refresh = $$props.refresh);
if ('mind' in $$props) mind = $$props.mind;
if ('conver' in $$props) $$invalidate(2, conver = $$props.conver);
if ('keys' in $$props) keys = $$props.keys;
if ('handleKeydown' in $$props) $$invalidate(10, handleKeydown = $$props.handleKeydown);
if ('compiler' in $$props) compiler = $$props.compiler;
if ('compiled' in $$props) $$invalidate(11, compiled = $$props.compiled);
if ('flub' in $$props) flub = $$props.flub;
if ('look' in $$props) $$invalidate(3, look = $$props.look);
if ('junk' in $$props) junk = $$props.junk;
};

if ($$props && "$$inject" in $$props) {
$$self.$inject_state($$props.$$inject);
}

$$self.$$.update = () => {
if ($$self.$$.dirty[0] & /*moment*/ 8192) {
$: (moment, sipd.sync());
}

if ($$self.$$.dirty[0] & /*refresh, con*/ 3) {
$: refresh && sipd.refreshN(con.c.wake);
}
};

return [
con,
refresh,
conver,
look,
$sto,
parser,
b,
bleep,
bloop,
reconver,
handleKeydown,
compiled,
dobla,
moment,
click_handler,
click_handler_1,
click_handler_2
];
}

... it goes on.
We must do our compiling earlier!

I tried rearranging this list already:
const config: UserConfig = {
plugins: [
compilePlugin(),
sveltekit()
]
};

But hang on...

Lets read more docs.

Where To Plug In Language Transformers

While searching for ordering these plugins, I find various places, none focused on extending|compiling-to javascript, though that seems to be implied in the functioning of my next approach:

svelte.config.js:
import adapter from '@sveltejs/adapter-auto';
import {stylehouse_lite} from './src/lib/Compile.js'
import sveltePreprocess from 'svelte-preprocess';

/** @type {import('@sveltejs/kit').Config} */
const config = {
// Consult https://github.com/sveltejs/svelte-preprocess
// for more information about preprocessors
preprocess: sveltePreprocess({
aliases: [
['stlli', 'stylehouse_lite'],
],
stylehouse_lite({ content, filename, attributes }) {
const { code, map } = stylehouse_lite(content);

return { code, map };
},
}),

kit: {
adapter: adapter()
}
};

export default config;

Now we can write ourselves <script lang="stlli">

Gets us as far as:
/src/routes/Code.svelte:113:12 Unexpected token
/src/routes/Code.svelte:113:12
111 |  
 112 |      // this was a '# comment'
 113 |      let look:Le = undefined
                    ^
So it appears this is running our compiled javascript as javascript!

If we remove typescript syntax, it all works!
letz.git 34e1a1ebf500

However syntax hilighting in vscode doesn't know stlli.

take over the 'typescript' alias!

Hilighting is back on.
It does not transform .ts.
letz.git 9925a7dc80b
svelte.config.js:
preprocess: sveltePreprocess({
aliases: [
['typescript', 'stylehouse_lite'],
],
stylehouse_lite({ content, filename, attributes }) {
const { code, map } = stylehouse_lite(content);

return { code, map };
},
}),

atop Code.svelte:
<script lang="typescript">

I tried making it "ts" to include eg St.ts, but there's lots of typescript syntax around that needs compiling properly, after we compile stylehouse_lite.

lets get typescript working

To revise, I have hijacked the alias 'typescript', which is used by only one thing: Code.svelte
Here are two transforms in one:
preprocess: sveltePreprocess({
aliases: [
['typescript', 'stylehouse_lite'],
],
stylehouse_lite({ content, filename, attributes }) {
let { code, map } = stylehouse_lite(content)
console.log("Step un: "+filename,{ code, map })

//return { code, map };
let typescript = code;

({ code, map } = esbuild.transformSync(typescript, {
sourcemap: true,
sourcefile: filename
}))
console.log("Step two: "+filename,{ code, map })

return { code, map };
},
}),

letz.git afb2ed4e697c788

nd given some typescript (let look:Le = undefined) to chew on, logs:

two "Step un" (which look good)

two of these:

Error while preprocessing /app/src/routes/Code.svelte - Transform failed with 1 error:
/app/src/routes/Code.svelte:113:12: ERROR: Expected ";" but found ":"
Error: Transform failed with 1 error:
/app/src/routes/Code.svelte:113:12: ERROR: Expected ";" but found ":"
    at failureErrorWithLog (/app/node_modules/esbuild/lib/main.js:1566:15)
    at /app/node_modules/esbuild/lib/main.js:805:29
    at responseCallbacks.<computed> (/app/node_modules/esbuild/lib/main.js:671:9)
    at handleIncomingPacket (/app/node_modules/esbuild/lib/main.js:726:9)
    at Socket.readFromStdout (/app/node_modules/esbuild/lib/main.js:647:7)
    at Socket.emit (node:events:511:28)
    at addChunk (node:internal/streams/readable:332:12)
    at readableAddChunk (node:internal/streams/readable:305:9)
    at Readable.push (node:internal/streams/readable:242:10)
    at Pipe.onStreamRead (node:internal/stream_base_commons:190:23) 

 So perhaps there are two threads at work, both compiling Code.svelte at the same time, both blowing up at the first sight of typescript syntax.

If I put this in here:
({ code, map } = esbuild.transformSync(typescript, {
loader: 'ts',
 Then they look like:

9:15:46 AM [vite-plugin-svelte] /app/src/routes/Code.svelte:108:0 'Diring' is not defined
9:15:46 AM [vite-plugin-svelte] /app/src/routes/Code.svelte:112:9 'Con' is not defined
9:15:46 AM [vite-plugin-svelte] /app/src/routes/Code.svelte:120:0 'Codemirror' is not defined
9:15:46 AM [vite-plugin-svelte] /app/src/routes/Code.svelte:121:10 'Lezing' is not defined
9:15:46 AM [vite-plugin-svelte] /app/src/routes/Code.svelte:12:8 'page' is not defined
9:15:46 AM [vite-plugin-svelte] /app/src/routes/Code.svelte:12:8 'page' is not defined
9:15:46 AM [vite-plugin-svelte] /app/src/routes/Code.svelte:12:8 'page' is not defined
page is not defined
ReferenceError: page is not defined
    at /app/src/routes/Code.svelte:23:17
    at Object.$$render (/node_modules/svelte/internal/index.mjs:1871:22)
    at eval (/src/routes/+page.svelte:28:120)
    at Object.$$render (/node_modules/svelte/internal/index.mjs:1871:22)
    at Object.default (root.svelte:41:38)
    at eval (/src/routes/+layout.svelte:22:67)
    at Object.$$render (/node_modules/svelte/internal/index.mjs:1871:22)
    at root.svelte:40:37
    at $$render (/node_modules/svelte/internal/index.mjs:1871:22)
    at Object.render (/node_modules/svelte/internal/index.mjs:1879:26)

 But the typescript gets compiled! The second log message says:

    'let look = void 0;\n' +

What has it done to all the imports?

Here are the before|after splice-together with gaps:

import { onMount } from 'svelte';
'import { onMount } from "svelte";\n' +
import { page } from '$app/stores'

import { goto } from '$app/navigation'

    import { sto } from './stores.js';
'import { sto } from "./stores.js";\n' +
import { Le } from "$lib/Le"
'import { Le } from "$lib/Le";\n' +
import { St_main, St_loop, ex } from "$lib/St"
'import { St_main, St_loop, ex } from "$lib/St";\n' +
import { Construct, sip_dispatch } from "$lib/Co"
'import { Construct, sip_dispatch } from "$lib/Co";\n' +
import Diring from "$lib/Diring.svelte"

import Con from '$lib/pi/Con.svelte'

import { stylehouse_lite } from "$lib/Compile.js"
'import { stylehouse_lite } from "$lib/Compile.js";\n' +

It has dropped all components!

Guess since they are not used in the <script> we are compiling here - we use them in markup: <Diring t="Direr"/> etc.

It also dropped:

goto - whose use was commented out, a legit forget.
It was changing the url to be a cursor: 

a.set('ierorag',dat.i) 
goto(`?${a.toString()}`)   

page - was being used to access the url, inverse of the above.
 
let a = $page.url.searchParams 

Typescript compiling doesn't alter this line, then we ReferenceError: page is not defined while SSRing (presumably?), giving the client a 500 error.

These are all Svelte features!

There must be some way to compile Sveltey typescript without losing the imports!

Here's a way someone got something working at some point.

My flood of research also included vite-plugin-svelte.git, which I code search and find it uses esbuild like we now shall:

({ code, map } = esbuild.transformSync(typescript, {
loader: 'ts',
tsconfigRaw: {
compilerOptions: {
// svelte typescript needs this flag to work with type imports
importsNotUsedAsValues: 'preserve',
preserveValueImports: true
}
},

And it then notices the obvious error: random_name is not defined

This occurs after typescript compile (Step Two has been said) I believe at javascript runtime, ie the line of code is evaluated... This line:

let vlat = random_name.thing.tola

Which occurs a few lines before that which caused page is not defined so I have no idea why it didn't happen earlier.

Fix that and...

typescript is working! 

Time to nice-up our piping of { code, map } along a few transforms...


what's it like without a sourcemap

letz.git d301381dcbd06d
In the debugger, you see the source:
# this was a '# comment'
let look = undefined
'one thing'
'thince blatant'
debugger
function dobla({detail:{view}}) {
Yet you have paused on the line of function dobla!

This is because 'one thing' compiles to two things:
let look = void 0;
"un thing";
"two thing";
"thince blon_itn";
debugger;

function dobla({ detail: { view } }) {
But without a sourcemap nothing tells you the lines move.
I'm going to insert a lot of lines, especially with the '... and ...' and 'each k+v' babble.

diff-match-patch would probably make one up.

Also it seems Svelte likes to standardise on ", having ;, blank line before function, no comments.

Anyway.

Someone out there says...

Use the APIs provided by the Mozilla source-map project.

Use the applySourceMap method. It does the following:

Applies a SourceMap for a source file to the SourceMap. Each mapping to the supplied source file is rewritten using the supplied SourceMap. Note: The resolution for the resulting mappings is the minimum of this map and the supplied map.

Some very trustworthy language, eg "the minimum of"


I already have source-map-js, a fork of the above.
In the README it says:
var rawSourceMap = {
version: 3,
file: 'min.js',
names: ['bar', 'baz', 'n'],
sources: ['one.js', 'two.js'],
sourceRoot: 'http://example.com/www/js/',
mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA'
};
This is the type of thing the maps are,
var smc = new SourceMapConsumer(rawSourceMap);
and each must be turned into a SourceMapConsumer as is shown here:
svelte.config.js
import adapter from '@sveltejs/adapter-auto';
import {stylehouse_lite} from './src/lib/Compile.js'
import sveltePreprocess from 'svelte-preprocess';
import esbuild from 'esbuild';
import {SourceMapConsumer,SourceMapGenerator} from 'source-map-js';

/** @type {import('@sveltejs/kit').Config} */
const config = {
preprocess: sveltePreprocess({
aliases: [
['typescript', 'stylehouse_lite'],
['ts', 'stylehouse_lite'],
],
stylehouse_lite({ content, filename, attributes }) {
console.log("stylehouse_lite: "+filename)

// compile stylehouse lite
let { code, map } = stylehouse_lite(content)
map.file = filename
// console.log("Step un: "+filename,{ code })
let typescript = code
let map1 = map;

// compile typescript
// < #jsgoof there has to be brackets around this whole statement:
({ code, map } = esbuild.transformSync(typescript, {
loader: 'ts',
tsconfigRaw: {
compilerOptions: {
// svelte typescript needs this flag to work with type imports
importsNotUsedAsValues: 'preserve',
preserveValueImports: true
}
},
sourcemap: true,
sourcefile: filename
}))
// console.log("Step two: "+filename,{ code })
let map2 = map

// combine sourcemaps
var aggregatedMap = SourceMapGenerator.fromSourceMap(new SourceMapConsumer(map2));
aggregatedMap.applySourceMap(new SourceMapConsumer(map1));
// toJSON makes data ready to become JSON
map = aggregatedMap.toJSON()
// for some reason this has: sources: [ 'null', '/app/src/routes/Code.svelte' ],
if (map.sources[0] == 'null') {
// your browser will fetch 'null'
map.sources[0] = map.sources[1]
if (map.sourcesContent[0] == null) {
// map.sourcesContent.shift()
}
else {
throw "not always null sources*"
}
}
// console.log("map: "+filename,{map })

return { code, map };
},
}),

kit: {
adapter: adapter()
}
};

export default config;

letz.git c1eac4492c9966
(this got refactored later)

and now it goes

letz.git 971a24cc4722cc

So svelte.config.js is where to specify a preprocess for .svelte files,

and you must also compile the ts

and vite.config.ts is where to plugin a plugin for .ts files.

that you leave ts

Yes, a slightly different connection for .ts compiling. I put my two compiler plugins together in Compiler.js to keep esbuild out of the Compile.js lingo filler the browser should also play with as we figure out more features there!


I notice .svelte files in DevTools are uncompiled (prettier syntax), whereas .ts are.
    Perhaps setting map.source = code helps?
    If I had an AI assistant I would wander it over there overnight.


HMR does not here

letz.git 25ed829d5e6b6487
Compile.js:
export function stylehouse_lite (source) {
     if (typeof source != 'string') throw "!string"
    console.log("en stylehouse_lite #####################")

Then I change it to
letz.git d05fede4b7b0a3bcc7

    console.log("en stylehouse_lite ########iii#########")

And this is coming out in the console as it live updates:

en stylehouse_lite ########iii#########
en stylehouse_lite #####################
en stylehouse_lite #####################
en stylehouse_lite ########iii#########
en stylehouse_lite ########iii#########
en stylehouse_lite ########iii#########

letz.git 99e825dd339d

More noise to identify where these calls come from, agent=svelte|vite|???

Then Answers Congeal

Turns out our svelte preprocess doesn't update the code it is running, whereas vite does.

vite restarts - but not enough to refresh the svelte preprocess

This should be no problem outside of development, which is happening in Code.svelte!

Done


I eventually stopped doing any compiling in .svelte files because the sourcemap was so wrong.

The sourcemap for .ts files doesn't work at all, DevTools just shows you the compiled stuff.

It's probably better to derive a sourcemap from diff-match-patch, as mentioned above, rather than from every text change you make... No time to investigate further.


So thanks everyone.