Compare commits

..
Author SHA1 Message Date
koloml 866a295dbb Other smaller changes for Vite 8 2026-10-17 04:51:55 +02:00
koloml 9c5ef143e7 Implement slightly hacky plugin to convert CJS to AMD modules 2026-10-17 04:51:33 +02:00
koloml 5435e055c3 Replacing amd output format with cjs
Vite 8 no longer support that. I will have to implement plugin to
convert modules into `amd` myself.
2026-10-16 22:39:42 +02:00
koloml 2b337e2fd6 Updated vite from 7.3.7 to 8.3.4
Also updated `@sveltejs/vite-plugin-svelte` to support newer version of
Vite
2026-10-16 22:36:57 +02:00
11 changed files with 673 additions and 869 deletions

No files matched your search

+9 -10
View File
@@ -4,6 +4,7 @@ import path from "path";
import fs from "fs";
import { SwapDefinedVariablesPlugin } from "../plugins/swap-defined-variables.js";
import { ScssViteReadEnvVariableFunctionPlugin } from "../plugins/scss-read-env-variable-function.js";
import { ConvertCjsToAmd } from "../plugins/convert-cjs-to-amd.js";
/**
* Create the result base file name for the file.
@@ -37,7 +38,7 @@ function wrapScriptIntoIIFE() {
Object.keys(bundle).forEach(fileName => {
const file = bundle[fileName];
file.code = `(() => {\n${file.code}})();`
file.code = `(()=>{${file.code}})();`
});
}
}
@@ -188,19 +189,16 @@ export async function buildScriptsAndStyles(buildOptions) {
configFile: false,
publicDir: false,
build: {
rollupOptions: {
rolldownOptions: {
input: amdScriptsInput,
output: {
dir: buildOptions.outputDir,
entryFileNames: '[name].js',
chunkFileNames: 'chunks/[name]-[hash].js',
// ManifestV3 doesn't allow to use modern ES modules syntax, so we build all content scripts as AMD modules.
format: "amd",
inlineDynamicImports: false,
amd: {
// amd-lite requires names even for the entry-point scripts, so we should make sure to add those.
autoId: true,
},
// Vite 8 no longer have support for `amd` format extension depends on
// TODO Make plugin to live-convert cjs module require/exports into AMD
format: "cjs",
codeSplitting: true,
// All these modules are not intended to be used outside of extension anyway
minifyInternalExports: true,
}
@@ -211,6 +209,7 @@ export async function buildScriptsAndStyles(buildOptions) {
alias: aliasesSettings,
},
plugins: [
ConvertCjsToAmd(),
wrapScriptIntoIIFE(),
collectDependenciesForManifestBuilding((fileName, dependencies) => {
pathsReplacementByOutputPath
@@ -228,7 +227,7 @@ export async function buildScriptsAndStyles(buildOptions) {
configFile: false,
publicDir: false,
build: {
rollupOptions: {
rolldownOptions: {
input: libsAndStylesInput,
output: {
dir: buildOptions.outputDir,
+170
View File
@@ -0,0 +1,170 @@
/**
* # CJS → AMD module format converter.
*
* Incredibly scary and potentially unstable way to bring back AMD modules on Vite 8+. Converts require() calls into
* list of dependencies and forms define() function with module name, dependencies and exports.
*
* @return {import('vite').Plugin}
*/
export function ConvertCjsToAmd() {
const requireAssignmentsRegExp = /(?:const\s*|,\s*)(?<variable>[a-z0-9_$]+)\s*=\s*require\((?<path>['"][^'"]+['"])\)/iy;
const inlineRequireRegExp = /require\((?<path>['"][^'"]+['"])\)\.(?<variable>[a-z0-9_$]+)/gi
const exportsDefinitionRegExp = /Object.defineProperty\(exports,['"](?<export_variable>[^'"]+)['"],{enumerable:!\d,get:function\(\){return\s*(?<assigned_variable>[^};]+)}}\)(?<separator>[,;])/gi;
const exportVariablePrefix = "$";
const exportVariableCollisionsRegExp = /\$[a-z_$]+/g;
const amdExportsModule = '"exports"';
/**
* Clears the name of the module to be fit with what we had before.
* @param {string} path Path to normalize.
* @returns {string} Normalized path.
*/
function normalizeDependencyName(path) {
return path.replace(/\.js(['"])$/, "$1");
}
/**
* Deduce the potentially unused variable from the current code. Uses special rarely encountered prefix.
* @param {string} code Code to parse the used symbols from. Ideally, code should be passed here before any
* replacements.
* @param {Set<string>} usedSymbols Set of symbols which were already used or found in code.
* @returns {string} Not-yet used variable which is likely safe to use.
*/
function findUnusedSpecialVariable(code, usedSymbols) {
// Run complete scan of the source code first, to find any potential collisions.
if (!usedSymbols.size) {
let match;
exportVariableCollisionsRegExp.lastIndex = 0;
while (match = exportVariableCollisionsRegExp.exec(code)) {
usedSymbols.add(match[0]);
}
}
if (!usedSymbols.size || !usedSymbols.has(exportVariablePrefix)) {
usedSymbols.add(exportVariablePrefix);
return exportVariablePrefix;
}
let count = 0;
let resultVariable;
do {
resultVariable = exportVariablePrefix + count.toString(36);
} while (count++ < 1000 && usedSymbols.has(resultVariable));
if (usedSymbols.has(resultVariable)) {
throw new Error('Failed to convert CJS to AMD: Failed to find non-colliding variable after 100 tries!');
}
usedSymbols.add(resultVariable);
return resultVariable;
}
/**
* Main function doing the conversion of one chunk.
* @param {import('rolldown').OutputChunk} chunk
*/
function convertChunkToAmd(chunk) {
/** @type {string} */
let chunkCode = chunk.code;
let lastRequireIndex = 0;
/**
* Saved per-chunk set of used symbols. This set is reused for multiple calls to not parse code multiple times.
* @type {Set<string>}
*/
const chunkUsedSymbols = new Set();
/** @type {Record<string, string>} */
let dependenciesVarsToPaths = {};
// region Extracting `const x = require()` calls from the start of the source code...
do {
requireAssignmentsRegExp.lastIndex = lastRequireIndex;
const result = requireAssignmentsRegExp.exec(chunkCode);
if (!result) {
break;
}
dependenciesVarsToPaths[result.groups.variable] = normalizeDependencyName(result.groups.path);
lastRequireIndex += result["0"].length;
} while (true);
if (lastRequireIndex > 0) {
const characterAfterRequires = chunkCode[lastRequireIndex];
if (characterAfterRequires !== ';') {
throw new Error("Failed to convert CJS to AMD: reached last require() assignment, but list didn't stop.");
}
}
// endregion
// region Finding and replacing inlined `require().x` calls...
chunkCode = chunkCode.replaceAll(inlineRequireRegExp, (match, dependencyPath, variable) => {
const dependencyVariable = findUnusedSpecialVariable(chunkCode, chunkUsedSymbols);
dependenciesVarsToPaths[dependencyVariable] = dependencyPath;
return `${dependencyVariable}.${variable}`;
});
// endregion
// region Finding and replacing all exports with AMD exports...
/** @type {string|undefined} */
let chunkExportsVariable;
exportsDefinitionRegExp.lastIndex = 0;
chunkCode = chunkCode.replaceAll(exportsDefinitionRegExp, (substring, exportVariable, assignedVariable, separator) => {
chunkExportsVariable ??= findUnusedSpecialVariable(chunkCode, chunkUsedSymbols);
if (!(chunkExportsVariable in dependenciesVarsToPaths)) {
dependenciesVarsToPaths = {
[chunkExportsVariable]: amdExportsModule,
...dependenciesVarsToPaths,
};
}
return `${chunkExportsVariable}.${exportVariable}=${assignedVariable}${separator}`;
});
// endregion
const normalizedModuleName = normalizeDependencyName(`"${chunk.fileName}"`);
const dependenciesVariables = Object.keys(dependenciesVarsToPaths).join(',');
const dependenciesPaths = Object.values(dependenciesVarsToPaths).join(',');
if (lastRequireIndex > 0) {
chunkCode = chunkCode.substring(lastRequireIndex + 1);
}
chunk.code = `define(${normalizedModuleName},[${dependenciesPaths}],function(${dependenciesVariables}){${chunkCode}})`;
}
return {
name: "koloml:covert-cjs-to-amd",
apply: "build",
enforce: "pre",
/**
* @param _
* @param {Record<string, (import('rolldown').OutputBundle|import('rolldown').OutputChunk)>} bundle
*/
generateBundle(_, bundle) {
for (const chunkId in bundle) {
const chunkOrAsset = bundle[chunkId];
if (chunkOrAsset.type === 'asset') {
continue;
}
convertChunkToAmd(chunkOrAsset);
}
}
}
}
+1 -2
View File
@@ -30,8 +30,7 @@
],
"js": [
"src/content/deps/amd.ts",
"src/content/user-details.ts",
"src/content/fun.ts"
"src/content/user-details.ts"
]
},
{
+489 -760
View File
File diff suppressed because it is too large. Load diff
+2 -2
View File
@@ -24,7 +24,7 @@
"svelte": "^5.57.2"
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^6.2.4",
"@sveltejs/vite-plugin-svelte": "^7.3.1",
"@types/chrome": "^0.3.5",
"@types/node": "^25.9.9",
"@vitest/coverage-v8": "^4.1.11",
@@ -33,7 +33,7 @@
"jsdom": "^29.1.1",
"svelte-check": "^4.7.6",
"typescript": "^6.0.3",
"vite": "^7.3.7",
"vite": "^8.3.4",
"vitest": "^4.1.11"
},
"engines": {
-34
View File
@@ -1,34 +0,0 @@
import roundFeatherFontUrl from "../assets/icon/fonts/roundfeather-regular-1.001.ttf?url";
import FunPreferences from "$lib/extension/preferences/FunPreferences";
const preferences = new FunPreferences();
const url = (browser ?? chrome).runtime.getURL(roundFeatherFontUrl);
const avaliFontDefinitionStyleTag = document.createElement('style');
// language=CSS
avaliFontDefinitionStyleTag.textContent = `
@font-face {
font-family: Roundfeather;
url('${url}') format('truetype');
}
:root {
--font-family-base: Roundfeather, verdana, arial, helvetica, sans-serif !important;
--font-family-monospace: Roundfeather, "Droid Sans Mono", monospace !important;
}
`;
function refreshAvaliFont(isFontEnabled: boolean) {
if (!isFontEnabled) {
avaliFontDefinitionStyleTag.remove();
return;
}
document.head.append(avaliFontDefinitionStyleTag);
}
preferences.subscribe(settings => {
refreshAvaliFont(settings.useAvaliFont ?? false);
});
preferences.useAvaliFont.get().then(refreshAvaliFont);
@@ -1,16 +0,0 @@
import CacheablePreferences, { PreferenceField, type WithFields } from "$lib/extension/base/CacheablePreferences";
interface FunPreferencesFields {
useAvaliFont: boolean;
}
export default class FunPreferences extends CacheablePreferences<FunPreferencesFields> implements WithFields<FunPreferencesFields> {
constructor() {
super('fun');
}
readonly useAvaliFont = new PreferenceField(this, {
field: "useAvaliFont",
defaultValue: false,
});
}
-1
View File
@@ -11,7 +11,6 @@
<hr>
<MenuItem href="/preferences/tags">Tagging</MenuItem>
<MenuItem href="/preferences/misc">Misc & Tools</MenuItem>
<MenuItem href="/preferences/fun">Fun</MenuItem>
<hr>
<MenuItem href="/preferences/debug">Debug</MenuItem>
</Menu>
-31
View File
@@ -1,31 +0,0 @@
<script>
import Menu from "$components/ui/menu/Menu.svelte";
import MenuItem from "$components/ui/menu/MenuItem.svelte";
import FormContainer from "$components/ui/forms/FormContainer.svelte";
import FormControl from "$components/ui/forms/FormControl.svelte";
import CheckboxField from "$components/ui/forms/CheckboxField.svelte";
import { useAvaliFont } from "$stores/preferences/fun";
import Notice from "$components/ui/Notice.svelte";
</script>
<Menu>
<MenuItem href="/preferences" icon="arrow-left">Back</MenuItem>
<hr>
</Menu>
<Notice level="warning">
These features are not to mean be really useful. Just a little bit of fun.
</Notice>
<Menu>
<hr>
</Menu>
<FormContainer>
<FormControl>
<CheckboxField bind:checked={$useAvaliFont}>
<strong>Display everything in Avali Scratch</strong><br>
<small>
This will apply the special font across the whole website, making it unusable unless you can read Avali Scratch
alphabet. Unfortunately, text will flicker on every page load.
</small>
</CheckboxField>
</FormControl>
</FormContainer>
-11
View File
@@ -1,11 +0,0 @@
import FunPreferences from "$lib/extension/preferences/FunPreferences";
import { PreferenceSync } from "$lib/store-sync";
import { writable } from "svelte/store";
export const useAvaliFont = writable<boolean>(false);
const preferences = new FunPreferences();
void PreferenceSync.for(preferences)
.connect(preferences.useAvaliFont, useAvaliFont, {updateToStore: Boolean})
.startSync()
+2 -2
View File
@@ -1,7 +1,7 @@
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vitest/config';
import { ScssViteReadEnvVariableFunctionPlugin } from "./.vite/plugins/scss-read-env-variable-function";
import { SwapDefinedVariablesPlugin } from "./.vite/plugins/swap-defined-variables";
import { ScssViteReadEnvVariableFunctionPlugin } from "./.vite/plugins/scss-read-env-variable-function.js";
import { SwapDefinedVariablesPlugin } from "./.vite/plugins/swap-defined-variables.js";
export default defineConfig(() => {
return {