diff --git a/bin/build_package.ts b/bin/build_package.ts index 08c079b56d2..c773838429a 100644 --- a/bin/build_package.ts +++ b/bin/build_package.ts @@ -5,10 +5,9 @@ import * as fs from 'node:fs'; import * as path from 'node:path'; import { parseArgs } from 'node:util'; -import * as LightningCSS from 'lightningcss'; -import * as rollup from 'rollup'; import { globSync } from 'tinyglobby'; -import { getRollupConfiguration } from './rollup.ts'; +import { build } from 'tsup'; +import { readPackageJSON } from "pkg-types"; const args = parseArgs({ allowPositionals: true, @@ -34,117 +33,105 @@ async function main() { process.exit(1); } - const packageData = await import(path.join(packageRoot, 'package.json'), {with: { type: 'json'}}); - const packageName = packageData.name; - const srcDir = path.join(packageRoot, 'src'); - const distDir = path.join(packageRoot, 'dist'); + const packageData = await readPackageJSON(path.join(packageRoot, 'package.json')); + const isStimulusBundle = '@symfony/stimulus-bundle' === packageData.name; + const isReactOrVueOrSvelte = ['@symfony/ux-react', '@symfony/ux-vue', '@symfony/ux-svelte'].some(name => packageData.name.startsWith(name)); - if (!fs.existsSync(srcDir)) { - console.error(`The package directory "${packageRoot}" does not contain a "src" directory.`); - process.exit(1); - } - - if (fs.existsSync(distDir)) { - console.log(`Cleaning up the "${distDir}" directory...`); - await fs.promises.rm(distDir, { recursive: true }); - await fs.promises.mkdir(distDir); - } - - const inputScriptFiles = [ - ...globSync(path.join(srcDir, '*controller.ts')), - ...(['@symfony/ux-react', '@symfony/ux-vue', '@symfony/ux-svelte'].includes(packageName) - ? [path.join(srcDir, 'loader.ts'), path.join(srcDir, 'components.ts')] - : []), - ...(packageName === '@symfony/stimulus-bundle' - ? [path.join(srcDir, 'loader.ts'), path.join(srcDir, 'controllers.ts')] - : []), + const inputCssFile = packageData?.config?.css_source; + const inputFiles = [ + ...globSync('src/*controller.ts'), + ...(isStimulusBundle ? ['src/loader.ts', 'src/controllers.ts'] : []), + ...(isReactOrVueOrSvelte ? ['src/loader.ts', 'src/components.ts'] : []), + ...(inputCssFile ? [inputCssFile] : []), ]; - const inputStyleFile = packageData.config?.css_source; - const buildCss = async () => { - if (!inputStyleFile) { - return; + const external = new Set([ + // We force "dependencies" and "peerDependencies" to be external to avoid bundling them. + ...Object.keys(packageData.dependencies || {}), + ...Object.keys(packageData.peerDependencies || {}), + ]); + + inputFiles.forEach((file) => { + // custom handling for StimulusBundle + if (file.includes('StimulusBundle/assets/src/loader.ts')) { + external.add('./controllers.js'); } - const inputStyleFileDist = path.resolve(distDir, `${path.basename(inputStyleFile, '.css')}.min.css`); - - console.log('Minifying CSS...'); - const css = await fs.promises.readFile(inputStyleFile, 'utf-8'); - const { code: minified } = LightningCSS.transform({ - filename: path.basename(inputStyleFile, '.css'), - code: Buffer.from(css), - minify: true, - sourceMap: false, // TODO: Maybe we can add source maps later? :) - }); - await fs.promises.writeFile(inputStyleFileDist, minified); - }; - - if (inputScriptFiles.length === 0) { - console.error( - `No input files found for package "${packageName}" (directory "${packageRoot}").\nEnsure you have at least a file matching the pattern "src/*_controller.ts", or manually specify input files in "${import.meta.filename}" file.` - ); - process.exit(1); - } - const rollupConfig = getRollupConfiguration({ - packageRoot, - inputFiles: inputScriptFiles, - isWatch, - additionalPlugins: [ - ...(isWatch && inputStyleFile - ? [ - { - name: 'watcher', - buildStart(this: rollup.PluginContext) { - this.addWatchFile(inputStyleFile); - }, - }, - ] - : []), - ], + // React, Vue, Svelte + if (file.includes('assets/src/loader.ts')) { + external.add('./components.js'); + } }); - if (isWatch) { - console.log( - `Watching for JavaScript${inputStyleFile ? ' and CSS' : ''} files modifications in "${srcDir}" directory...` - ); - - const watcher = rollup.watch(rollupConfig); - watcher.on('event', (event) => { - if (event.code === 'ERROR') { - console.error('Error during build:', event.error); - } - - if ((event.code === 'BUNDLE_END' || event.code === 'ERROR') && event.result) { - event.result.close(); - } - }); - watcher.on('change', async (id, { event }) => { - if (event === 'update') { - console.log('Files were modified, rebuilding...'); - } - - if (inputStyleFile && id === inputStyleFile) { - await buildCss(); + await build({ + entry: inputFiles, + outDir: path.join(packageRoot, 'dist'), + clean: true, + external: Array.from(external), + format: 'esm', + platform: 'browser', + tsconfig: path.join(import.meta.dirname, '../tsconfig.packages.json'), + dts: { + entry: inputFiles.filter(inputFile => !inputFile.endsWith('.css')), + }, + watch: isWatch, + splitting: false, + esbuildOptions(options) { + // Disabling `bundle` option prevent esbuild to inline relative (but external) imports (like "./components.js" for React, Vue, Svelte). + options.bundle = !(isStimulusBundle || isReactOrVueOrSvelte); + }, + plugins: [ + { + /** + * This plugin is used to minify CSS files using LightningCSS. + * + * Even if tsup supports CSS minification through ESBuild by setting the `minify: true` option, + * it also minifies JS files but we don't want that. + */ + name: 'symfony-ux:minify-css', + async renderChunk(code, chunkInfo) { + if (!/\.css$/.test(chunkInfo.path)) { + return null; + } + + const { transform } = await import('lightningcss'); + const result = transform({ + filename: chunkInfo.path, + code: Buffer.from(code), + minify: true, + }); + + console.log(`[Symfony UX] Minified CSS file: ${chunkInfo.path}`); + + return { + code: result.code.toString(), + map: result.map ? result.map.toString() : null, + } + }, + }, + + /** + * Unlike tsdown/rolldown and the option "cssEntryFileNames", tsup does not support + * customizing the output file names for CSS files. + * A plugin is needed to rename the written CSS files to add the ".min" suffix. + */ + { + name: 'symfony-ux:append-min-to-css', + async buildEnd({ writtenFiles }) { + for (const writtenFile of writtenFiles) { + if (!writtenFile.name.endsWith('.css')) { + continue; + } + + const newName = writtenFile.name.replace(/\.css$/, '.min.css'); + await fs.promises.rename(writtenFile.name, newName); + + console.info(`[Symfony UX] Renamed ${writtenFile.name} to ${newName}`); + } + } } - }); - } else { - console.log(`Building JavaScript files from ${packageName} package...`); - const start = Date.now(); - - if (typeof rollupConfig.output === 'undefined' || Array.isArray(rollupConfig.output)) { - console.error( - `The rollup configuration for package "${packageName}" does not contain a valid output configuration.` - ); - process.exit(1); - } - - const bundle = await rollup.rollup(rollupConfig); - await bundle.write(rollupConfig.output); - - await buildCss(); - - console.log(`Done in ${((Date.now() - start) / 1000).toFixed(3)} seconds.`); - } + ], + }); } main(); diff --git a/package.json b/package.json index 92f574c8434..47246ef5d10 100644 --- a/package.json +++ b/package.json @@ -14,16 +14,14 @@ }, "devDependencies": { "@biomejs/biome": "^2.0.4", - "@rollup/plugin-commonjs": "^28.0.6", - "@rollup/plugin-node-resolve": "^16.0.1", - "@rollup/plugin-typescript": "^12.1.4", "@testing-library/dom": "catalog:", "@testing-library/jest-dom": "catalog:", "@types/node": "^22.6.0", "lightningcss": "^1.28.2", + "pkg-types": "^2.2.0", "playwright": "^1.47.0", - "rollup": "^4.44.1", "tinyglobby": "^0.2.14", + "tsup": "^8.5.0", "vitest": "catalog:" }, "version": "2.27.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 80d495e19ac..1811efb3955 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -49,15 +49,6 @@ importers: '@biomejs/biome': specifier: ^2.0.4 version: 2.1.2 - '@rollup/plugin-commonjs': - specifier: ^28.0.6 - version: 28.0.6(rollup@4.45.1) - '@rollup/plugin-node-resolve': - specifier: ^16.0.1 - version: 16.0.1(rollup@4.45.1) - '@rollup/plugin-typescript': - specifier: ^12.1.4 - version: 12.1.4(rollup@4.45.1)(tslib@2.8.1)(typescript@5.8.3) '@testing-library/dom': specifier: 'catalog:' version: 10.4.0 @@ -70,15 +61,18 @@ importers: lightningcss: specifier: ^1.28.2 version: 1.30.1 + pkg-types: + specifier: ^2.2.0 + version: 2.2.0 playwright: specifier: ^1.47.0 version: 1.54.1 - rollup: - specifier: ^4.44.1 - version: 4.45.1 tinyglobby: specifier: ^0.2.14 version: 0.2.14 + tsup: + specifier: ^8.5.0 + version: 8.5.0(postcss@8.5.6)(tsx@4.20.3)(typescript@5.8.3) vitest: specifier: 'catalog:' version: 3.2.4(@types/node@22.16.5)(@vitest/browser@3.2.4)(jsdom@26.1.0)(lightningcss@1.30.1)(msw@2.10.4(@types/node@22.16.5)(typescript@5.8.3))(terser@5.43.1) @@ -1316,6 +1310,10 @@ packages: '@types/node': optional: true + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + '@jest/types@26.6.2': resolution: {integrity: sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==} engines: {node: '>= 10.14.2'} @@ -1358,52 +1356,16 @@ packages: '@orchidjs/unicode-variants@1.1.2': resolution: {integrity: sha512-5DobW1CHgnBROOEpFlEXytED5OosEWESFvg/VYmH0143oXcijYTprRYJTs+55HzGM4IqxiLFSuqEzu9mPNwVsA==} + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} '@rolldown/pluginutils@1.0.0-beta.27': resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} - '@rollup/plugin-commonjs@28.0.6': - resolution: {integrity: sha512-XSQB1K7FUU5QP+3lOQmVCE3I0FcbbNvmNT4VJSj93iUjayaARrTQeoRdiYQoftAJBLrR9t2agwAd3ekaTgHNlw==} - engines: {node: '>=16.0.0 || 14 >= 14.17'} - peerDependencies: - rollup: ^2.68.0||^3.0.0||^4.0.0 - peerDependenciesMeta: - rollup: - optional: true - - '@rollup/plugin-node-resolve@16.0.1': - resolution: {integrity: sha512-tk5YCxJWIG81umIvNkSod2qK5KyQW19qcBF/B78n1bjtOON6gzKoVeSzAE8yHCZEDmqkHKkxplExA8KzdJLJpA==} - engines: {node: '>=14.0.0'} - peerDependencies: - rollup: ^2.78.0||^3.0.0||^4.0.0 - peerDependenciesMeta: - rollup: - optional: true - - '@rollup/plugin-typescript@12.1.4': - resolution: {integrity: sha512-s5Hx+EtN60LMlDBvl5f04bEiFZmAepk27Q+mr85L/00zPDn1jtzlTV6FWn81MaIwqfWzKxmOJrBWHU6vtQyedQ==} - engines: {node: '>=14.0.0'} - peerDependencies: - rollup: ^2.14.0||^3.0.0||^4.0.0 - tslib: '*' - typescript: '>=3.7.0' - peerDependenciesMeta: - rollup: - optional: true - tslib: - optional: true - - '@rollup/pluginutils@5.2.0': - resolution: {integrity: sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==} - engines: {node: '>=14.0.0'} - peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 - peerDependenciesMeta: - rollup: - optional: true - '@rollup/rollup-android-arm-eabi@4.45.1': resolution: {integrity: sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==} cpu: [arm] @@ -1643,9 +1605,6 @@ packages: '@types/react@18.3.23': resolution: {integrity: sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w==} - '@types/resolve@1.20.2': - resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} - '@types/statuses@2.0.6': resolution: {integrity: sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==} @@ -1780,6 +1739,10 @@ packages: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} + ansi-regex@6.1.0: + resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} + engines: {node: '>=12'} + ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} @@ -1788,6 +1751,13 @@ packages: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} engines: {node: '>=10'} + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + aria-query@4.2.2: resolution: {integrity: sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==} engines: {node: '>=6.0'} @@ -1810,6 +1780,12 @@ packages: resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} engines: {node: '>= 0.4'} + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + browserslist@4.25.1: resolution: {integrity: sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -1818,6 +1794,12 @@ packages: buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + bundle-require@5.1.0: + resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + peerDependencies: + esbuild: '>=0.18' + cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} @@ -1853,6 +1835,10 @@ packages: resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} engines: {node: '>= 16'} + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + cli-width@4.1.0: resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} engines: {node: '>= 12'} @@ -1878,8 +1864,19 @@ packages: commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} - commondir@1.0.1: - resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} + commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + + confbox@0.2.2: + resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==} + + consola@3.4.2: + resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} + engines: {node: ^14.18.0 || >=16.10.0} convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -1897,6 +1894,10 @@ packages: cross-fetch@3.2.0: resolution: {integrity: sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==} + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + css-tree@2.3.1: resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} @@ -1963,12 +1964,18 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + electron-to-chromium@1.5.187: resolution: {integrity: sha512-cl5Jc9I0KGUoOoSbxvTywTa40uspGJt/BDBoDLoxJRSBpWh4FFXBsjNRHfQrONsV/OoEjDfHUmZQa2d6Ze4YgA==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -2020,6 +2027,9 @@ packages: resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} engines: {node: '>=12.0.0'} + exsolve@1.0.7: + resolution: {integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -2034,6 +2044,13 @@ packages: picomatch: optional: true + fix-dts-default-cjs-exports@1.0.1: + resolution: {integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==} + + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + form-data@4.0.4: resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} engines: {node: '>= 6'} @@ -2070,6 +2087,10 @@ packages: get-tsconfig@4.10.1: resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==} + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true + gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} @@ -2123,32 +2144,32 @@ packages: intl-messageformat@10.7.16: resolution: {integrity: sha512-UmdmHUmp5CIKKjSoE10la5yfU+AYJAaiYLsodbjL4lji83JNvgOQUjGaGhGrpFCb0Uh7sl7qfP1IyILa8Z40ug==} - is-core-module@2.16.1: - resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} - engines: {node: '>= 0.4'} - is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - is-module@1.0.0: - resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} - is-node-process@1.2.0: resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==} is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} - is-reference@1.2.1: - resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} - is-reference@3.0.3: resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + jest-canvas-mock@2.5.2: resolution: {integrity: sha512-vgnpPupjOL6+L5oJXzxTxFrlGEIbHdZqFU+LFNdtLxZ3lRDCl17FlTMM7IatoRQkrcyOTMlDinjUguqmQ6bR2A==} + joycon@3.1.1: + resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} + engines: {node: '>=10'} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -2248,6 +2269,17 @@ packages: resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==} engines: {node: '>= 12.0.0'} + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + engines: {node: '>=14'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + load-tsconfig@0.2.5: + resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + loader-utils@3.3.1: resolution: {integrity: sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==} engines: {node: '>= 12.13.0'} @@ -2255,6 +2287,9 @@ packages: locate-character@3.0.0: resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} + lodash.sortby@4.7.0: + resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} + lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} @@ -2297,6 +2332,17 @@ packages: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} engines: {node: '>=4'} + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + mlly@1.7.4: + resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==} + moo-color@1.0.3: resolution: {integrity: sha512-i/+ZKXMDf6aqYtBhuOcej71YSlbjT3wCO/4H1j8rPvxDJEifdwgg5MaFyu6iYAT8GBZJg2z0dkgK4YMzvURALQ==} @@ -2321,6 +2367,9 @@ packages: resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} engines: {node: ^18.17.0 || >=20.5.0} + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -2341,6 +2390,10 @@ packages: nwsapi@2.2.20: resolution: {integrity: sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==} + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + opencollective-postinstall@2.0.3: resolution: {integrity: sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==} hasBin: true @@ -2348,11 +2401,19 @@ packages: outvariant@1.4.3: resolution: {integrity: sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==} + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + parse5@7.3.0: resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} - path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} path-to-regexp@6.3.0: resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} @@ -2374,6 +2435,16 @@ packages: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} + engines: {node: '>= 6'} + + pkg-types@1.3.1: + resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + + pkg-types@2.2.0: + resolution: {integrity: sha512-2SM/GZGAEkPp3KWORxQZns4M+WSeXbC2HEvmOIJe3Cmiv6ieAJvdVhDldtHqM5J1Y7MrR1XhkBT/rMlhh9FdqQ==} + playwright-core@1.54.1: resolution: {integrity: sha512-Nbjs2zjj0htNhzgiy5wu+3w09YetDx5pkrpI/kZotDlDUaYk0HVA5xrBVPdow4SAUIlhgKcJeJg4GRKW6xHusA==} engines: {node: '>=18'} @@ -2384,6 +2455,24 @@ packages: engines: {node: '>=18'} hasBin: true + postcss-load-config@6.0.1: + resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} + engines: {node: '>= 18'} + peerDependencies: + jiti: '>=1.21.0' + postcss: '>=8.0.9' + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + jiti: + optional: true + postcss: + optional: true + tsx: + optional: true + yaml: + optional: true + postcss@8.5.6: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} @@ -2422,6 +2511,10 @@ packages: resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + redent@3.0.0: resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} engines: {node: '>=8'} @@ -2440,14 +2533,13 @@ packages: resize-observer-polyfill@1.5.1: resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - resolve@1.22.10: - resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} - engines: {node: '>= 0.4'} - hasBin: true - rollup@4.45.1: resolution: {integrity: sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -2474,6 +2566,14 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} @@ -2496,6 +2596,10 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} + source-map@0.8.0-beta.0: + resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} + engines: {node: '>= 8'} + stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} @@ -2513,10 +2617,18 @@ packages: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + strip-indent@3.0.0: resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} engines: {node: '>=8'} @@ -2524,14 +2636,15 @@ packages: strip-literal@3.0.0: resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==} + sucrase@3.35.0: + resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} - supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} - svelte-hmr@0.15.3: resolution: {integrity: sha512-41snaPswvSf8TJUhlkoJBekRrABDXDMdpNpT2tfHIv4JuhgvHqLMhEPGtaQn0BmbNSTkuz2Ed20DF2eHw0SmBQ==} engines: {node: ^12.20 || ^14.13.1 || >= 16} @@ -2553,6 +2666,13 @@ packages: engines: {node: '>=10'} hasBin: true + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -2600,13 +2720,42 @@ packages: tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + tr46@1.0.1: + resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} + tr46@5.1.1: resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} engines: {node: '>=18'} + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + + ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + tsup@8.5.0: + resolution: {integrity: sha512-VmBp77lWNQq6PfuMqCHD3xWl22vEoWsKajkF8t+yMBawlUS8JzEI+vOVMeuNZIuMML8qXRizFKi9oD5glKQVcQ==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + '@microsoft/api-extractor': ^7.36.0 + '@swc/core': ^1 + postcss: ^8.4.12 + typescript: '>=4.5.0' + peerDependenciesMeta: + '@microsoft/api-extractor': + optional: true + '@swc/core': + optional: true + postcss: + optional: true + typescript: + optional: true + tsx@4.20.3: resolution: {integrity: sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==} engines: {node: '>=18.0.0'} @@ -2631,6 +2780,9 @@ packages: engines: {node: '>=14.17'} hasBin: true + ufo@1.6.1: + resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} + undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} @@ -2745,6 +2897,9 @@ packages: webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + webidl-conversions@4.0.2: + resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} + webidl-conversions@7.0.0: resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} engines: {node: '>=12'} @@ -2764,6 +2919,14 @@ packages: whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + whatwg-url@7.1.0: + resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + why-is-node-running@2.3.0: resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} engines: {node: '>=8'} @@ -2777,6 +2940,10 @@ packages: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + ws@8.18.3: resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} engines: {node: '>=10.0.0'} @@ -3234,6 +3401,15 @@ snapshots: '@types/node': 22.16.5 optional: true + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + '@jest/types@26.6.2': dependencies: '@types/istanbul-lib-coverage': 2.0.6 @@ -3292,49 +3468,13 @@ snapshots: '@orchidjs/unicode-variants@1.1.2': {} + '@pkgjs/parseargs@0.11.0': + optional: true + '@polka/url@1.0.0-next.29': {} '@rolldown/pluginutils@1.0.0-beta.27': {} - '@rollup/plugin-commonjs@28.0.6(rollup@4.45.1)': - dependencies: - '@rollup/pluginutils': 5.2.0(rollup@4.45.1) - commondir: 1.0.1 - estree-walker: 2.0.2 - fdir: 6.4.6(picomatch@4.0.3) - is-reference: 1.2.1 - magic-string: 0.30.17 - picomatch: 4.0.3 - optionalDependencies: - rollup: 4.45.1 - - '@rollup/plugin-node-resolve@16.0.1(rollup@4.45.1)': - dependencies: - '@rollup/pluginutils': 5.2.0(rollup@4.45.1) - '@types/resolve': 1.20.2 - deepmerge: 4.3.1 - is-module: 1.0.0 - resolve: 1.22.10 - optionalDependencies: - rollup: 4.45.1 - - '@rollup/plugin-typescript@12.1.4(rollup@4.45.1)(tslib@2.8.1)(typescript@5.8.3)': - dependencies: - '@rollup/pluginutils': 5.2.0(rollup@4.45.1) - resolve: 1.22.10 - typescript: 5.8.3 - optionalDependencies: - rollup: 4.45.1 - tslib: 2.8.1 - - '@rollup/pluginutils@5.2.0(rollup@4.45.1)': - dependencies: - '@types/estree': 1.0.8 - estree-walker: 2.0.2 - picomatch: 4.0.3 - optionalDependencies: - rollup: 4.45.1 - '@rollup/rollup-android-arm-eabi@4.45.1': optional: true @@ -3576,8 +3716,6 @@ snapshots: '@types/prop-types': 15.7.15 csstype: 3.1.3 - '@types/resolve@1.20.2': {} - '@types/statuses@2.0.6': optional: true @@ -3752,12 +3890,18 @@ snapshots: ansi-regex@5.0.1: {} + ansi-regex@6.1.0: {} + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 ansi-styles@5.2.0: {} + ansi-styles@6.2.1: {} + + any-promise@1.3.0: {} + aria-query@4.2.2: dependencies: '@babel/runtime': 7.27.6 @@ -3775,6 +3919,12 @@ snapshots: axobject-query@4.1.0: {} + balanced-match@1.0.2: {} + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + browserslist@4.25.1: dependencies: caniuse-lite: 1.0.30001727 @@ -3785,6 +3935,11 @@ snapshots: buffer-from@1.1.2: optional: true + bundle-require@5.1.0(esbuild@0.25.8): + dependencies: + esbuild: 0.25.8 + load-tsconfig: 0.2.5 + cac@6.7.14: {} call-bind-apply-helpers@1.0.2: @@ -3820,6 +3975,10 @@ snapshots: check-error@2.1.1: {} + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + cli-width@4.1.0: optional: true @@ -3851,7 +4010,13 @@ snapshots: commander@2.20.3: optional: true - commondir@1.0.1: {} + commander@4.1.1: {} + + confbox@0.1.8: {} + + confbox@0.2.2: {} + + consola@3.4.2: {} convert-source-map@2.0.0: {} @@ -3868,6 +4033,12 @@ snapshots: transitivePeerDependencies: - encoding + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + css-tree@2.3.1: dependencies: mdn-data: 2.0.30 @@ -3919,10 +4090,13 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 + eastasianwidth@0.2.0: {} + electron-to-chromium@1.5.187: {} - emoji-regex@8.0.0: - optional: true + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} entities@4.5.0: {} @@ -4010,6 +4184,8 @@ snapshots: expect-type@1.2.2: {} + exsolve@1.0.7: {} + fast-deep-equal@3.1.3: {} fast-uri@3.0.6: {} @@ -4018,6 +4194,17 @@ snapshots: optionalDependencies: picomatch: 4.0.3 + fix-dts-default-cjs-exports@1.0.1: + dependencies: + magic-string: 0.30.17 + mlly: 1.7.4 + rollup: 4.45.1 + + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + form-data@4.0.4: dependencies: asynckit: 0.4.0 @@ -4061,6 +4248,15 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 + glob@10.4.5: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + gopd@1.2.0: {} graphql@16.11.0: @@ -4114,33 +4310,32 @@ snapshots: '@formatjs/icu-messageformat-parser': 2.11.2 tslib: 2.8.1 - is-core-module@2.16.1: - dependencies: - hasown: 2.0.2 - - is-fullwidth-code-point@3.0.0: - optional: true - - is-module@1.0.0: {} + is-fullwidth-code-point@3.0.0: {} is-node-process@1.2.0: optional: true is-potential-custom-element-name@1.0.1: {} - is-reference@1.2.1: + is-reference@3.0.3: dependencies: '@types/estree': 1.0.8 - is-reference@3.0.3: + isexe@2.0.0: {} + + jackspeak@3.4.3: dependencies: - '@types/estree': 1.0.8 + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 jest-canvas-mock@2.5.2: dependencies: cssfontparser: 1.2.1 moo-color: 1.0.3 + joycon@3.1.1: {} + js-tokens@4.0.0: {} js-tokens@9.0.1: {} @@ -4227,10 +4422,18 @@ snapshots: lightningcss-win32-arm64-msvc: 1.30.1 lightningcss-win32-x64-msvc: 1.30.1 + lilconfig@3.1.3: {} + + lines-and-columns@1.2.4: {} + + load-tsconfig@0.2.5: {} + loader-utils@3.3.1: {} locate-character@3.0.0: {} + lodash.sortby@4.7.0: {} + lodash@4.17.21: {} loose-envify@1.4.0: @@ -4263,6 +4466,19 @@ snapshots: min-indent@1.0.1: {} + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + minipass@7.1.2: {} + + mlly@1.7.4: + dependencies: + acorn: 8.15.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.1 + moo-color@1.0.3: dependencies: color-name: 1.1.4 @@ -4300,6 +4516,12 @@ snapshots: mute-stream@2.0.0: optional: true + mz@2.7.0: + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + nanoid@3.3.11: {} node-fetch@2.7.0: @@ -4310,16 +4532,25 @@ snapshots: nwsapi@2.2.20: {} + object-assign@4.1.1: {} + opencollective-postinstall@2.0.3: {} outvariant@1.4.3: optional: true + package-json-from-dist@1.0.1: {} + parse5@7.3.0: dependencies: entities: 6.0.1 - path-parse@1.0.7: {} + path-key@3.1.1: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.2 path-to-regexp@6.3.0: optional: true @@ -4338,6 +4569,20 @@ snapshots: picomatch@4.0.3: {} + pirates@4.0.7: {} + + pkg-types@1.3.1: + dependencies: + confbox: 0.1.8 + mlly: 1.7.4 + pathe: 2.0.3 + + pkg-types@2.2.0: + dependencies: + confbox: 0.2.2 + exsolve: 1.0.7 + pathe: 2.0.3 + playwright-core@1.54.1: {} playwright@1.54.1: @@ -4346,6 +4591,13 @@ snapshots: optionalDependencies: fsevents: 2.3.2 + postcss-load-config@6.0.1(postcss@8.5.6)(tsx@4.20.3): + dependencies: + lilconfig: 3.1.3 + optionalDependencies: + postcss: 8.5.6 + tsx: 4.20.3 + postcss@8.5.6: dependencies: nanoid: 3.3.11 @@ -4389,6 +4641,8 @@ snapshots: dependencies: loose-envify: 1.4.0 + readdirp@4.1.2: {} + redent@3.0.0: dependencies: indent-string: 4.0.0 @@ -4404,13 +4658,9 @@ snapshots: resize-observer-polyfill@1.5.1: {} - resolve-pkg-maps@1.0.0: {} + resolve-from@5.0.0: {} - resolve@1.22.10: - dependencies: - is-core-module: 2.16.1 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 + resolve-pkg-maps@1.0.0: {} rollup@4.45.1: dependencies: @@ -4459,10 +4709,15 @@ snapshots: semver@6.3.1: {} + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + siginfo@2.0.0: {} - signal-exit@4.1.0: - optional: true + signal-exit@4.1.0: {} sirv@3.0.1: dependencies: @@ -4481,6 +4736,10 @@ snapshots: source-map@0.6.1: optional: true + source-map@0.8.0-beta.0: + dependencies: + whatwg-url: 7.1.0 + stackback@0.0.2: {} statuses@2.0.2: @@ -4496,12 +4755,20 @@ snapshots: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - optional: true + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 - optional: true + + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.1.0 strip-indent@3.0.0: dependencies: @@ -4511,12 +4778,20 @@ snapshots: dependencies: js-tokens: 9.0.1 + sucrase@3.35.0: + dependencies: + '@jridgewell/gen-mapping': 0.3.12 + commander: 4.1.1 + glob: 10.4.5 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.7 + ts-interface-checker: 0.1.13 + supports-color@7.2.0: dependencies: has-flag: 4.0.0 - supports-preserve-symlinks-flag@1.0.0: {} - svelte-hmr@0.15.3(svelte@4.2.20): dependencies: svelte: 4.2.20 @@ -4553,6 +4828,14 @@ snapshots: source-map-support: 0.5.21 optional: true + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + tinybench@2.9.0: {} tinyexec@0.3.2: {} @@ -4595,12 +4878,48 @@ snapshots: tr46@0.0.3: {} + tr46@1.0.1: + dependencies: + punycode: 2.3.1 + tr46@5.1.1: dependencies: punycode: 2.3.1 + tree-kill@1.2.2: {} + + ts-interface-checker@0.1.13: {} + tslib@2.8.1: {} + tsup@8.5.0(postcss@8.5.6)(tsx@4.20.3)(typescript@5.8.3): + dependencies: + bundle-require: 5.1.0(esbuild@0.25.8) + cac: 6.7.14 + chokidar: 4.0.3 + consola: 3.4.2 + debug: 4.4.1 + esbuild: 0.25.8 + fix-dts-default-cjs-exports: 1.0.1 + joycon: 3.1.1 + picocolors: 1.1.1 + postcss-load-config: 6.0.1(postcss@8.5.6)(tsx@4.20.3) + resolve-from: 5.0.0 + rollup: 4.45.1 + source-map: 0.8.0-beta.0 + sucrase: 3.35.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.14 + tree-kill: 1.2.2 + optionalDependencies: + postcss: 8.5.6 + typescript: 5.8.3 + transitivePeerDependencies: + - jiti + - supports-color + - tsx + - yaml + tsx@4.20.3: dependencies: esbuild: 0.25.8 @@ -4620,6 +4939,8 @@ snapshots: typescript@5.8.3: {} + ufo@1.6.1: {} + undici-types@6.21.0: {} universalify@0.2.0: @@ -4738,6 +5059,8 @@ snapshots: webidl-conversions@3.0.1: {} + webidl-conversions@4.0.2: {} + webidl-conversions@7.0.0: {} whatwg-encoding@3.1.1: @@ -4756,6 +5079,16 @@ snapshots: tr46: 0.0.3 webidl-conversions: 3.0.1 + whatwg-url@7.1.0: + dependencies: + lodash.sortby: 4.7.0 + tr46: 1.0.1 + webidl-conversions: 4.0.2 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + why-is-node-running@2.3.0: dependencies: siginfo: 2.0.0 @@ -4773,7 +5106,12 @@ snapshots: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - optional: true + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 ws@8.18.3: {} diff --git a/src/Autocomplete/assets/dist/controller.d.ts b/src/Autocomplete/assets/dist/controller.d.ts index d5cc6b3e272..e03b841b16d 100644 --- a/src/Autocomplete/assets/dist/controller.d.ts +++ b/src/Autocomplete/assets/dist/controller.d.ts @@ -1,13 +1,14 @@ import { Controller } from '@hotwired/stimulus'; import TomSelect from 'tom-select'; -export interface AutocompletePreConnectOptions { + +interface AutocompletePreConnectOptions { options: any; } -export interface AutocompleteConnectOptions { +interface AutocompleteConnectOptions { tomSelect: TomSelect; options: any; } -export default class extends Controller { +declare class export_default extends Controller { #private; static values: { url: StringConstructor; @@ -54,3 +55,5 @@ export default class extends Controller { private createOptionsDataStructure; private areOptionsEquivalent; } + +export { type AutocompleteConnectOptions, type AutocompletePreConnectOptions, export_default as default }; diff --git a/src/Autocomplete/assets/dist/controller.js b/src/Autocomplete/assets/dist/controller.js index c68b7bd3015..a96549af0a4 100644 --- a/src/Autocomplete/assets/dist/controller.js +++ b/src/Autocomplete/assets/dist/controller.js @@ -1,417 +1,412 @@ -import { Controller } from '@hotwired/stimulus'; -import TomSelect from 'tom-select'; - -/****************************************************************************** -Copyright (c) Microsoft Corporation. - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -PERFORMANCE OF THIS SOFTWARE. -***************************************************************************** */ -/* global Reflect, Promise, SuppressedError, Symbol, Iterator */ - - -function __classPrivateFieldGet(receiver, state, kind, f) { - if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); - if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); - return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); -} - -typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { - var e = new Error(message); - return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; +var __typeError = (msg) => { + throw TypeError(msg); }; +var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg); +var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj)); +var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); +var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method); -var _default_1_instances, _default_1_getCommonConfig, _default_1_createAutocomplete, _default_1_createAutocompleteWithHtmlContents, _default_1_createAutocompleteWithRemoteData, _default_1_stripTags, _default_1_mergeConfigs, _default_1_normalizePluginsToHash, _default_1_normalizePlugins, _default_1_createTomSelect; -class default_1 extends Controller { - constructor() { - super(...arguments); - _default_1_instances.add(this); - this.isObserving = false; - this.hasLoadedChoicesPreviously = false; - this.originalOptions = []; - _default_1_normalizePluginsToHash.set(this, (plugins) => { - if (Array.isArray(plugins)) { - return plugins.reduce((acc, plugin) => { - if (typeof plugin === 'string') { - acc[plugin] = {}; - } - if (typeof plugin === 'object' && plugin.name) { - acc[plugin.name] = plugin.options || {}; - } - return acc; - }, {}); - } - return plugins; - }); +// src/controller.ts +import { Controller } from "@hotwired/stimulus"; +import TomSelect from "tom-select"; +var _instances, getCommonConfig_fn, createAutocomplete_fn, createAutocompleteWithHtmlContents_fn, createAutocompleteWithRemoteData_fn, stripTags_fn, mergeConfigs_fn, _normalizePluginsToHash, normalizePlugins_fn, createTomSelect_fn; +var controller_default = class extends Controller { + constructor() { + super(...arguments); + __privateAdd(this, _instances); + this.isObserving = false; + this.hasLoadedChoicesPreviously = false; + this.originalOptions = []; + /** + * Normalizes the plugins to a hash, so that we can merge them easily. + */ + __privateAdd(this, _normalizePluginsToHash, (plugins) => { + if (Array.isArray(plugins)) { + return plugins.reduce((acc, plugin) => { + if (typeof plugin === "string") { + acc[plugin] = {}; + } + if (typeof plugin === "object" && plugin.name) { + acc[plugin.name] = plugin.options || {}; + } + return acc; + }, {}); + } + return plugins; + }); + } + initialize() { + if (!this.mutationObserver) { + this.mutationObserver = new MutationObserver((mutations) => { + this.onMutations(mutations); + }); } - initialize() { - if (!this.mutationObserver) { - this.mutationObserver = new MutationObserver((mutations) => { - this.onMutations(mutations); - }); - } + } + connect() { + if (this.selectElement) { + this.originalOptions = this.createOptionsDataStructure(this.selectElement); } - connect() { - if (this.selectElement) { - this.originalOptions = this.createOptionsDataStructure(this.selectElement); - } - this.initializeTomSelect(); + this.initializeTomSelect(); + } + initializeTomSelect() { + if (this.selectElement) { + this.selectElement.setAttribute("data-skip-morph", ""); } - initializeTomSelect() { - if (this.selectElement) { - this.selectElement.setAttribute('data-skip-morph', ''); - } - if (this.urlValue) { - this.tomSelect = __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_createAutocompleteWithRemoteData).call(this, this.urlValue, this.hasMinCharactersValue ? this.minCharactersValue : null); - return; - } - if (this.optionsAsHtmlValue) { - this.tomSelect = __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_createAutocompleteWithHtmlContents).call(this); - return; - } - this.tomSelect = __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_createAutocomplete).call(this); - this.startMutationObserver(); + if (this.urlValue) { + this.tomSelect = __privateMethod(this, _instances, createAutocompleteWithRemoteData_fn).call(this, this.urlValue, this.hasMinCharactersValue ? this.minCharactersValue : null); + return; } - disconnect() { - this.stopMutationObserver(); - let currentSelectedValues = []; - if (this.selectElement) { - if (this.selectElement.multiple) { - currentSelectedValues = Array.from(this.selectElement.options) - .filter((option) => option.selected) - .map((option) => option.value); - } - else { - currentSelectedValues = [this.selectElement.value]; - } - } - this.tomSelect.destroy(); - if (this.selectElement) { - if (this.selectElement.multiple) { - Array.from(this.selectElement.options).forEach((option) => { - option.selected = currentSelectedValues.includes(option.value); - }); - } - else { - this.selectElement.value = currentSelectedValues[0]; - } - } + if (this.optionsAsHtmlValue) { + this.tomSelect = __privateMethod(this, _instances, createAutocompleteWithHtmlContents_fn).call(this); + return; } - urlValueChanged() { - this.resetTomSelect(); + this.tomSelect = __privateMethod(this, _instances, createAutocomplete_fn).call(this); + this.startMutationObserver(); + } + disconnect() { + this.stopMutationObserver(); + let currentSelectedValues = []; + if (this.selectElement) { + if (this.selectElement.multiple) { + currentSelectedValues = Array.from(this.selectElement.options).filter((option) => option.selected).map((option) => option.value); + } else { + currentSelectedValues = [this.selectElement.value]; + } } - getMaxOptions() { - return this.selectElement ? this.selectElement.options.length : 50; + this.tomSelect.destroy(); + if (this.selectElement) { + if (this.selectElement.multiple) { + Array.from(this.selectElement.options).forEach((option) => { + option.selected = currentSelectedValues.includes(option.value); + }); + } else { + this.selectElement.value = currentSelectedValues[0]; + } } - get selectElement() { - if (!(this.element instanceof HTMLSelectElement)) { - return null; - } - return this.element; + } + urlValueChanged() { + this.resetTomSelect(); + } + getMaxOptions() { + return this.selectElement ? this.selectElement.options.length : 50; + } + /** + * Returns the element, but only if it's a select element. + */ + get selectElement() { + if (!(this.element instanceof HTMLSelectElement)) { + return null; } - get formElement() { - if (!(this.element instanceof HTMLInputElement) && !(this.element instanceof HTMLSelectElement)) { - throw new Error('Autocomplete Stimulus controller can only be used on an or or ` element, + * where, when its value changes, the parent is notified. + */ + notifyParentModelChange(modelName, value) { + const parentComponent = findParent(this.component); + if (!parentComponent) { + return; + } + this.parentModelBindings.forEach((modelBinding) => { + const childModelName = modelBinding.innerModelName || "value"; + if (childModelName !== modelName) { + return; + } + parentComponent.set(modelBinding.modelName, value, modelBinding.shouldRender, modelBinding.debounce); + }); + } + getChildren() { + return findChildren(this.component); + } +}; - /** - * @param possibleActiveElement - * @param ctx - * @returns {boolean} - */ - function ignoreValueOfActiveElement(possibleActiveElement, ctx) { - return ctx.ignoreActiveValue && possibleActiveElement === document.activeElement; - } +// src/Component/plugins/LazyPlugin.ts +var LazyPlugin_default = class { + constructor() { + this.intersectionObserver = null; + } + attachToComponent(component) { + if ("lazy" !== component.element.attributes.getNamedItem("loading")?.value) { + return; + } + component.on("connect", () => { + this.getObserver().observe(component.element); + }); + component.on("disconnect", () => { + this.intersectionObserver?.unobserve(component.element); + }); + } + getObserver() { + if (!this.intersectionObserver) { + this.intersectionObserver = new IntersectionObserver((entries, observer) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + entry.target.dispatchEvent(new CustomEvent("live:appear")); + observer.unobserve(entry.target); + } + }); + }); + } + return this.intersectionObserver; + } +}; - /** - * @param oldNode root node to merge content into - * @param newContent new content to merge - * @param ctx the merge context - * @returns {Element} the element that ended up in the DOM - */ - function morphOldNodeTo(oldNode, newContent, ctx) { - if (ctx.ignoreActive && oldNode === document.activeElement) ; else if (newContent == null) { - if (ctx.callbacks.beforeNodeRemoved(oldNode) === false) return oldNode; +// src/Component/plugins/LoadingPlugin.ts +var LoadingPlugin_default = class { + attachToComponent(component) { + component.on("loading.state:started", (element, request) => { + this.startLoading(component, element, request); + }); + component.on("loading.state:finished", (element) => { + this.finishLoading(component, element); + }); + this.finishLoading(component, component.element); + } + startLoading(component, targetElement, backendRequest) { + this.handleLoadingToggle(component, true, targetElement, backendRequest); + } + finishLoading(component, targetElement) { + this.handleLoadingToggle(component, false, targetElement, null); + } + handleLoadingToggle(component, isLoading, targetElement, backendRequest) { + if (isLoading) { + this.addAttributes(targetElement, ["busy"]); + } else { + this.removeAttributes(targetElement, ["busy"]); + } + this.getLoadingDirectives(component, targetElement).forEach(({ element, directives }) => { + if (isLoading) { + this.addAttributes(element, ["data-live-is-loading"]); + } else { + this.removeAttributes(element, ["data-live-is-loading"]); + } + directives.forEach((directive) => { + this.handleLoadingDirective(element, isLoading, directive, backendRequest); + }); + }); + } + handleLoadingDirective(element, isLoading, directive, backendRequest) { + const finalAction = parseLoadingAction(directive.action, isLoading); + const targetedActions = []; + const targetedModels = []; + let delay = 0; + const validModifiers = /* @__PURE__ */ new Map(); + validModifiers.set("delay", (modifier) => { + if (!isLoading) { + return; + } + delay = modifier.value ? Number.parseInt(modifier.value) : 200; + }); + validModifiers.set("action", (modifier) => { + if (!modifier.value) { + throw new Error( + `The "action" in data-loading must have an action name - e.g. action(foo). It's missing for "${directive.getString()}"` + ); + } + targetedActions.push(modifier.value); + }); + validModifiers.set("model", (modifier) => { + if (!modifier.value) { + throw new Error( + `The "model" in data-loading must have an action name - e.g. model(foo). It's missing for "${directive.getString()}"` + ); + } + targetedModels.push(modifier.value); + }); + directive.modifiers.forEach((modifier) => { + if (validModifiers.has(modifier.name)) { + const callable = validModifiers.get(modifier.name) ?? (() => { + }); + callable(modifier); + return; + } + throw new Error( + `Unknown modifier "${modifier.name}" used in data-loading="${directive.getString()}". Available modifiers are: ${Array.from(validModifiers.keys()).join(", ")}.` + ); + }); + if (isLoading && targetedActions.length > 0 && backendRequest && !backendRequest.containsOneOfActions(targetedActions)) { + return; + } + if (isLoading && targetedModels.length > 0 && backendRequest && !backendRequest.areAnyModelsUpdated(targetedModels)) { + return; + } + let loadingDirective; + switch (finalAction) { + case "show": + loadingDirective = () => this.showElement(element); + break; + case "hide": + loadingDirective = () => this.hideElement(element); + break; + case "addClass": + loadingDirective = () => this.addClass(element, directive.args); + break; + case "removeClass": + loadingDirective = () => this.removeClass(element, directive.args); + break; + case "addAttribute": + loadingDirective = () => this.addAttributes(element, directive.args); + break; + case "removeAttribute": + loadingDirective = () => this.removeAttributes(element, directive.args); + break; + default: + throw new Error(`Unknown data-loading action "${finalAction}"`); + } + if (delay) { + window.setTimeout(() => { + if (backendRequest && !backendRequest.isResolved) { + loadingDirective(); + } + }, delay); + return; + } + loadingDirective(); + } + getLoadingDirectives(component, element) { + const loadingDirectives = []; + let matchingElements = [...Array.from(element.querySelectorAll("[data-loading]"))]; + matchingElements = matchingElements.filter((elt) => elementBelongsToThisComponent(elt, component)); + if (element.hasAttribute("data-loading")) { + matchingElements = [element, ...matchingElements]; + } + matchingElements.forEach((element2) => { + if (!(element2 instanceof HTMLElement) && !(element2 instanceof SVGElement)) { + throw new Error("Invalid Element Type"); + } + const directives = parseDirectives(element2.dataset.loading || "show"); + loadingDirectives.push({ + element: element2, + directives + }); + }); + return loadingDirectives; + } + showElement(element) { + element.style.display = "revert"; + } + hideElement(element) { + element.style.display = "none"; + } + addClass(element, classes) { + element.classList.add(...combineSpacedArray(classes)); + } + removeClass(element, classes) { + element.classList.remove(...combineSpacedArray(classes)); + if (element.classList.length === 0) { + element.removeAttribute("class"); + } + } + addAttributes(element, attributes) { + attributes.forEach((attribute) => { + element.setAttribute(attribute, ""); + }); + } + removeAttributes(element, attributes) { + attributes.forEach((attribute) => { + element.removeAttribute(attribute); + }); + } +}; +var parseLoadingAction = (action, isLoading) => { + switch (action) { + case "show": + return isLoading ? "show" : "hide"; + case "hide": + return isLoading ? "hide" : "show"; + case "addClass": + return isLoading ? "addClass" : "removeClass"; + case "removeClass": + return isLoading ? "removeClass" : "addClass"; + case "addAttribute": + return isLoading ? "addAttribute" : "removeAttribute"; + case "removeAttribute": + return isLoading ? "removeAttribute" : "addAttribute"; + } + throw new Error(`Unknown data-loading action "${action}"`); +}; - oldNode.remove(); - ctx.callbacks.afterNodeRemoved(oldNode); - return null; - } else if (!isSoftMatch(oldNode, newContent)) { - if (ctx.callbacks.beforeNodeRemoved(oldNode) === false) return oldNode; - if (ctx.callbacks.beforeNodeAdded(newContent) === false) return oldNode; +// src/Component/plugins/PageUnloadingPlugin.ts +var PageUnloadingPlugin_default = class { + constructor() { + this.isConnected = false; + } + attachToComponent(component) { + component.on("render:started", (html, response, controls) => { + if (!this.isConnected) { + controls.shouldRender = false; + } + }); + component.on("connect", () => { + this.isConnected = true; + }); + component.on("disconnect", () => { + this.isConnected = false; + }); + } +}; - oldNode.parentElement.replaceChild(newContent, oldNode); - ctx.callbacks.afterNodeAdded(newContent); - ctx.callbacks.afterNodeRemoved(oldNode); - return newContent; - } else { - if (ctx.callbacks.beforeNodeMorphed(oldNode, newContent) === false) return oldNode; +// src/PollingDirector.ts +var PollingDirector_default = class { + constructor(component) { + this.isPollingActive = true; + this.pollingIntervals = []; + this.component = component; + } + addPoll(actionName, duration) { + this.polls.push({ actionName, duration }); + if (this.isPollingActive) { + this.initiatePoll(actionName, duration); + } + } + startAllPolling() { + if (this.isPollingActive) { + return; + } + this.isPollingActive = true; + this.polls.forEach(({ actionName, duration }) => { + this.initiatePoll(actionName, duration); + }); + } + stopAllPolling() { + this.isPollingActive = false; + this.pollingIntervals.forEach((interval) => { + clearInterval(interval); + }); + } + clearPolling() { + this.stopAllPolling(); + this.polls = []; + this.startAllPolling(); + } + initiatePoll(actionName, duration) { + let callback; + if (actionName === "$render") { + callback = () => { + this.component.render(); + }; + } else { + callback = () => { + this.component.action(actionName, {}, 0); + }; + } + const timer = window.setInterval(() => { + callback(); + }, duration); + this.pollingIntervals.push(timer); + } +}; - if (oldNode instanceof HTMLHeadElement && ctx.head.ignore) ; else if (oldNode instanceof HTMLHeadElement && ctx.head.style !== "morph") { - handleHeadElement(newContent, oldNode, ctx); - } else { - syncNodeFrom(newContent, oldNode, ctx); - if (!ignoreValueOfActiveElement(oldNode, ctx)) { - morphChildren(newContent, oldNode, ctx); - } - } - ctx.callbacks.afterNodeMorphed(oldNode, newContent); - return oldNode; +// src/Component/plugins/PollingPlugin.ts +var PollingPlugin_default = class { + attachToComponent(component) { + this.element = component.element; + this.pollingDirector = new PollingDirector_default(component); + this.initializePolling(); + component.on("connect", () => { + this.pollingDirector.startAllPolling(); + }); + component.on("disconnect", () => { + this.pollingDirector.stopAllPolling(); + }); + component.on("render:finished", () => { + this.initializePolling(); + }); + } + addPoll(actionName, duration) { + this.pollingDirector.addPoll(actionName, duration); + } + clearPolling() { + this.pollingDirector.clearPolling(); + } + initializePolling() { + this.clearPolling(); + if (this.element.dataset.poll === void 0) { + return; + } + const rawPollConfig = this.element.dataset.poll; + const directives = parseDirectives(rawPollConfig || "$render"); + directives.forEach((directive) => { + let duration = 2e3; + directive.modifiers.forEach((modifier) => { + switch (modifier.name) { + case "delay": + if (modifier.value) { + duration = Number.parseInt(modifier.value); } + break; + default: + console.warn(`Unknown modifier "${modifier.name}" in data-poll "${rawPollConfig}".`); } + }); + this.addPoll(directive.action, duration); + }); + } +}; - /** - * This is the core algorithm for matching up children. The idea is to use id sets to try to match up - * nodes as faithfully as possible. We greedily match, which allows us to keep the algorithm fast, but - * by using id sets, we are able to better match up with content deeper in the DOM. - * - * Basic algorithm is, for each node in the new content: - * - * - if we have reached the end of the old parent, append the new content - * - if the new content has an id set match with the current insertion point, morph - * - search for an id set match - * - if id set match found, morph - * - otherwise search for a "soft" match - * - if a soft match is found, morph - * - otherwise, prepend the new node before the current insertion point - * - * The two search algorithms terminate if competing node matches appear to outweigh what can be achieved - * with the current node. See findIdSetMatch() and findSoftMatch() for details. - * - * @param {Element} newParent the parent element of the new content - * @param {Element } oldParent the old content that we are merging the new content into - * @param ctx the merge context - */ - function morphChildren(newParent, oldParent, ctx) { - - let nextNewChild = newParent.firstChild; - let insertionPoint = oldParent.firstChild; - let newChild; +// src/Component/plugins/SetValueOntoModelFieldsPlugin.ts +var SetValueOntoModelFieldsPlugin_default = class { + attachToComponent(component) { + this.synchronizeValueOfModelFields(component); + component.on("render:finished", () => { + this.synchronizeValueOfModelFields(component); + }); + } + /** + * Sets the "value" of all model fields to the component data. + * + * This is called when the component initializes and after re-render. + * Take the following element: + * + * + * + * This method will set the "value" of that element to the value of + * the "firstName" model. + */ + synchronizeValueOfModelFields(component) { + component.element.querySelectorAll("[data-model]").forEach((element) => { + if (!(element instanceof HTMLElement)) { + throw new Error("Invalid element using data-model."); + } + if (element instanceof HTMLFormElement) { + return; + } + if (!elementBelongsToThisComponent(element, component)) { + return; + } + const modelDirective = getModelDirectiveFromElement(element); + if (!modelDirective) { + return; + } + const modelName = modelDirective.action; + if (component.getUnsyncedModels().includes(modelName)) { + return; + } + if (component.valueStore.has(modelName)) { + setValueOnElement(element, component.valueStore.get(modelName)); + } + if (element instanceof HTMLSelectElement && !element.multiple) { + component.valueStore.set(modelName, getValueFromElement(element, component.valueStore)); + } + }); + } +}; - // run through all the new content - while (nextNewChild) { +// src/Component/plugins/ValidatedFieldsPlugin.ts +var ValidatedFieldsPlugin_default = class { + attachToComponent(component) { + component.on("model:set", (modelName) => { + this.handleModelSet(modelName, component.valueStore); + }); + } + handleModelSet(modelName, valueStore) { + if (valueStore.has("validatedFields")) { + const validatedFields = [...valueStore.get("validatedFields")]; + if (!validatedFields.includes(modelName)) { + validatedFields.push(modelName); + } + valueStore.set("validatedFields", validatedFields); + } + } +}; - newChild = nextNewChild; - nextNewChild = newChild.nextSibling; - - // if we are at the end of the exiting parent's children, just append - if (insertionPoint == null) { - if (ctx.callbacks.beforeNodeAdded(newChild) === false) return; - - oldParent.appendChild(newChild); - ctx.callbacks.afterNodeAdded(newChild); - removeIdsFromConsideration(ctx, newChild); - continue; - } - - // if the current node has an id set match then morph - if (isIdSetMatch(newChild, insertionPoint, ctx)) { - morphOldNodeTo(insertionPoint, newChild, ctx); - insertionPoint = insertionPoint.nextSibling; - removeIdsFromConsideration(ctx, newChild); - continue; - } - - // otherwise search forward in the existing old children for an id set match - let idSetMatch = findIdSetMatch(newParent, oldParent, newChild, insertionPoint, ctx); - - // if we found a potential match, remove the nodes until that point and morph - if (idSetMatch) { - insertionPoint = removeNodesBetween(insertionPoint, idSetMatch, ctx); - morphOldNodeTo(idSetMatch, newChild, ctx); - removeIdsFromConsideration(ctx, newChild); - continue; - } - - // no id set match found, so scan forward for a soft match for the current node - let softMatch = findSoftMatch(newParent, oldParent, newChild, insertionPoint, ctx); - - // if we found a soft match for the current node, morph - if (softMatch) { - insertionPoint = removeNodesBetween(insertionPoint, softMatch, ctx); - morphOldNodeTo(softMatch, newChild, ctx); - removeIdsFromConsideration(ctx, newChild); - continue; - } - - // abandon all hope of morphing, just insert the new child before the insertion point - // and move on - if (ctx.callbacks.beforeNodeAdded(newChild) === false) return; - - oldParent.insertBefore(newChild, insertionPoint); - ctx.callbacks.afterNodeAdded(newChild); - removeIdsFromConsideration(ctx, newChild); - } - - // remove any remaining old nodes that didn't match up with new content - while (insertionPoint !== null) { - - let tempNode = insertionPoint; - insertionPoint = insertionPoint.nextSibling; - removeNode(tempNode, ctx); - } - } - - //============================================================================= - // Attribute Syncing Code - //============================================================================= - - /** - * @param attr {String} the attribute to be mutated - * @param to {Element} the element that is going to be updated - * @param updateType {("update"|"remove")} - * @param ctx the merge context - * @returns {boolean} true if the attribute should be ignored, false otherwise - */ - function ignoreAttribute(attr, to, updateType, ctx) { - if(attr === 'value' && ctx.ignoreActiveValue && to === document.activeElement){ - return true; - } - return ctx.callbacks.beforeAttributeUpdated(attr, to, updateType) === false; - } - - /** - * syncs a given node with another node, copying over all attributes and - * inner element state from the 'from' node to the 'to' node - * - * @param {Element} from the element to copy attributes & state from - * @param {Element} to the element to copy attributes & state to - * @param ctx the merge context - */ - function syncNodeFrom(from, to, ctx) { - let type = from.nodeType; - - // if is an element type, sync the attributes from the - // new node into the new node - if (type === 1 /* element type */) { - const fromAttributes = from.attributes; - const toAttributes = to.attributes; - for (const fromAttribute of fromAttributes) { - if (ignoreAttribute(fromAttribute.name, to, 'update', ctx)) { - continue; - } - if (to.getAttribute(fromAttribute.name) !== fromAttribute.value) { - to.setAttribute(fromAttribute.name, fromAttribute.value); - } - } - // iterate backwards to avoid skipping over items when a delete occurs - for (let i = toAttributes.length - 1; 0 <= i; i--) { - const toAttribute = toAttributes[i]; - if (ignoreAttribute(toAttribute.name, to, 'remove', ctx)) { - continue; - } - if (!from.hasAttribute(toAttribute.name)) { - to.removeAttribute(toAttribute.name); - } - } - } - - // sync text nodes - if (type === 8 /* comment */ || type === 3 /* text */) { - if (to.nodeValue !== from.nodeValue) { - to.nodeValue = from.nodeValue; - } - } - - if (!ignoreValueOfActiveElement(to, ctx)) { - // sync input values - syncInputValue(from, to, ctx); - } - } - - /** - * @param from {Element} element to sync the value from - * @param to {Element} element to sync the value to - * @param attributeName {String} the attribute name - * @param ctx the merge context - */ - function syncBooleanAttribute(from, to, attributeName, ctx) { - if (from[attributeName] !== to[attributeName]) { - let ignoreUpdate = ignoreAttribute(attributeName, to, 'update', ctx); - if (!ignoreUpdate) { - to[attributeName] = from[attributeName]; - } - if (from[attributeName]) { - if (!ignoreUpdate) { - to.setAttribute(attributeName, from[attributeName]); - } - } else { - if (!ignoreAttribute(attributeName, to, 'remove', ctx)) { - to.removeAttribute(attributeName); - } - } - } - } - - /** - * NB: many bothans died to bring us information: - * - * https://github.com/patrick-steele-idem/morphdom/blob/master/src/specialElHandlers.js - * https://github.com/choojs/nanomorph/blob/master/lib/morph.jsL113 - * - * @param from {Element} the element to sync the input value from - * @param to {Element} the element to sync the input value to - * @param ctx the merge context - */ - function syncInputValue(from, to, ctx) { - if (from instanceof HTMLInputElement && - to instanceof HTMLInputElement && - from.type !== 'file') { - - let fromValue = from.value; - let toValue = to.value; - - // sync boolean attributes - syncBooleanAttribute(from, to, 'checked', ctx); - syncBooleanAttribute(from, to, 'disabled', ctx); - - if (!from.hasAttribute('value')) { - if (!ignoreAttribute('value', to, 'remove', ctx)) { - to.value = ''; - to.removeAttribute('value'); - } - } else if (fromValue !== toValue) { - if (!ignoreAttribute('value', to, 'update', ctx)) { - to.setAttribute('value', fromValue); - to.value = fromValue; - } - } - } else if (from instanceof HTMLOptionElement) { - syncBooleanAttribute(from, to, 'selected', ctx); - } else if (from instanceof HTMLTextAreaElement && to instanceof HTMLTextAreaElement) { - let fromValue = from.value; - let toValue = to.value; - if (ignoreAttribute('value', to, 'update', ctx)) { - return; - } - if (fromValue !== toValue) { - to.value = fromValue; - } - if (to.firstChild && to.firstChild.nodeValue !== fromValue) { - to.firstChild.nodeValue = fromValue; - } - } - } - - //============================================================================= - // the HEAD tag can be handled specially, either w/ a 'merge' or 'append' style - //============================================================================= - function handleHeadElement(newHeadTag, currentHead, ctx) { - - let added = []; - let removed = []; - let preserved = []; - let nodesToAppend = []; - - let headMergeStyle = ctx.head.style; - - // put all new head elements into a Map, by their outerHTML - let srcToNewHeadNodes = new Map(); - for (const newHeadChild of newHeadTag.children) { - srcToNewHeadNodes.set(newHeadChild.outerHTML, newHeadChild); - } - - // for each elt in the current head - for (const currentHeadElt of currentHead.children) { - - // If the current head element is in the map - let inNewContent = srcToNewHeadNodes.has(currentHeadElt.outerHTML); - let isReAppended = ctx.head.shouldReAppend(currentHeadElt); - let isPreserved = ctx.head.shouldPreserve(currentHeadElt); - if (inNewContent || isPreserved) { - if (isReAppended) { - // remove the current version and let the new version replace it and re-execute - removed.push(currentHeadElt); - } else { - // this element already exists and should not be re-appended, so remove it from - // the new content map, preserving it in the DOM - srcToNewHeadNodes.delete(currentHeadElt.outerHTML); - preserved.push(currentHeadElt); - } - } else { - if (headMergeStyle === "append") { - // we are appending and this existing element is not new content - // so if and only if it is marked for re-append do we do anything - if (isReAppended) { - removed.push(currentHeadElt); - nodesToAppend.push(currentHeadElt); - } - } else { - // if this is a merge, we remove this content since it is not in the new head - if (ctx.head.shouldRemove(currentHeadElt) !== false) { - removed.push(currentHeadElt); - } - } - } - } - - // Push the remaining new head elements in the Map into the - // nodes to append to the head tag - nodesToAppend.push(...srcToNewHeadNodes.values()); - - let promises = []; - for (const newNode of nodesToAppend) { - let newElt = document.createRange().createContextualFragment(newNode.outerHTML).firstChild; - if (ctx.callbacks.beforeNodeAdded(newElt) !== false) { - if (newElt.href || newElt.src) { - let resolve = null; - let promise = new Promise(function (_resolve) { - resolve = _resolve; - }); - newElt.addEventListener('load', function () { - resolve(); - }); - promises.push(promise); - } - currentHead.appendChild(newElt); - ctx.callbacks.afterNodeAdded(newElt); - added.push(newElt); - } - } - - // remove all removed elements, after we have appended the new elements to avoid - // additional network requests for things like style sheets - for (const removedElement of removed) { - if (ctx.callbacks.beforeNodeRemoved(removedElement) !== false) { - currentHead.removeChild(removedElement); - ctx.callbacks.afterNodeRemoved(removedElement); - } - } - - ctx.head.afterHeadMorphed(currentHead, {added: added, kept: preserved, removed: removed}); - return promises; - } - - function noOp() { - } - - /* - Deep merges the config object and the Idiomoroph.defaults object to - produce a final configuration object - */ - function mergeDefaults(config) { - let finalConfig = {}; - // copy top level stuff into final config - Object.assign(finalConfig, defaults); - Object.assign(finalConfig, config); - - // copy callbacks into final config (do this to deep merge the callbacks) - finalConfig.callbacks = {}; - Object.assign(finalConfig.callbacks, defaults.callbacks); - Object.assign(finalConfig.callbacks, config.callbacks); - - // copy head config into final config (do this to deep merge the head) - finalConfig.head = {}; - Object.assign(finalConfig.head, defaults.head); - Object.assign(finalConfig.head, config.head); - return finalConfig; - } - - function createMorphContext(oldNode, newContent, config) { - config = mergeDefaults(config); - return { - target: oldNode, - newContent: newContent, - config: config, - morphStyle: config.morphStyle, - ignoreActive: config.ignoreActive, - ignoreActiveValue: config.ignoreActiveValue, - idMap: createIdMap(oldNode, newContent), - deadIds: new Set(), - callbacks: config.callbacks, - head: config.head - } - } - - function isIdSetMatch(node1, node2, ctx) { - if (node1 == null || node2 == null) { - return false; - } - if (node1.nodeType === node2.nodeType && node1.tagName === node2.tagName) { - if (node1.id !== "" && node1.id === node2.id) { - return true; - } else { - return getIdIntersectionCount(ctx, node1, node2) > 0; - } - } - return false; - } - - function isSoftMatch(node1, node2) { - if (node1 == null || node2 == null) { - return false; - } - return node1.nodeType === node2.nodeType && node1.tagName === node2.tagName - } - - function removeNodesBetween(startInclusive, endExclusive, ctx) { - while (startInclusive !== endExclusive) { - let tempNode = startInclusive; - startInclusive = startInclusive.nextSibling; - removeNode(tempNode, ctx); - } - removeIdsFromConsideration(ctx, endExclusive); - return endExclusive.nextSibling; - } - - //============================================================================= - // Scans forward from the insertionPoint in the old parent looking for a potential id match - // for the newChild. We stop if we find a potential id match for the new child OR - // if the number of potential id matches we are discarding is greater than the - // potential id matches for the new child - //============================================================================= - function findIdSetMatch(newContent, oldParent, newChild, insertionPoint, ctx) { - - // max id matches we are willing to discard in our search - let newChildPotentialIdCount = getIdIntersectionCount(ctx, newChild, oldParent); - - let potentialMatch = null; - - // only search forward if there is a possibility of an id match - if (newChildPotentialIdCount > 0) { - let potentialMatch = insertionPoint; - // if there is a possibility of an id match, scan forward - // keep track of the potential id match count we are discarding (the - // newChildPotentialIdCount must be greater than this to make it likely - // worth it) - let otherMatchCount = 0; - while (potentialMatch != null) { - - // If we have an id match, return the current potential match - if (isIdSetMatch(newChild, potentialMatch, ctx)) { - return potentialMatch; - } - - // computer the other potential matches of this new content - otherMatchCount += getIdIntersectionCount(ctx, potentialMatch, newContent); - if (otherMatchCount > newChildPotentialIdCount) { - // if we have more potential id matches in _other_ content, we - // do not have a good candidate for an id match, so return null - return null; - } - - // advanced to the next old content child - potentialMatch = potentialMatch.nextSibling; - } - } - return potentialMatch; - } - - //============================================================================= - // Scans forward from the insertionPoint in the old parent looking for a potential soft match - // for the newChild. We stop if we find a potential soft match for the new child OR - // if we find a potential id match in the old parents children OR if we find two - // potential soft matches for the next two pieces of new content - //============================================================================= - function findSoftMatch(newContent, oldParent, newChild, insertionPoint, ctx) { - - let potentialSoftMatch = insertionPoint; - let nextSibling = newChild.nextSibling; - let siblingSoftMatchCount = 0; - - while (potentialSoftMatch != null) { - - if (getIdIntersectionCount(ctx, potentialSoftMatch, newContent) > 0) { - // the current potential soft match has a potential id set match with the remaining new - // content so bail out of looking - return null; - } - - // if we have a soft match with the current node, return it - if (isSoftMatch(newChild, potentialSoftMatch)) { - return potentialSoftMatch; - } - - if (isSoftMatch(nextSibling, potentialSoftMatch)) { - // the next new node has a soft match with this node, so - // increment the count of future soft matches - siblingSoftMatchCount++; - nextSibling = nextSibling.nextSibling; - - // If there are two future soft matches, bail to allow the siblings to soft match - // so that we don't consume future soft matches for the sake of the current node - if (siblingSoftMatchCount >= 2) { - return null; - } - } - - // advanced to the next old content child - potentialSoftMatch = potentialSoftMatch.nextSibling; - } - - return potentialSoftMatch; - } - - function parseContent(newContent) { - let parser = new DOMParser(); - - // remove svgs to avoid false-positive matches on head, etc. - let contentWithSvgsRemoved = newContent.replace(/]*>|>)([\s\S]*?)<\/svg>/gim, ''); - - // if the newContent contains a html, head or body tag, we can simply parse it w/o wrapping - if (contentWithSvgsRemoved.match(/<\/html>/) || contentWithSvgsRemoved.match(/<\/head>/) || contentWithSvgsRemoved.match(/<\/body>/)) { - let content = parser.parseFromString(newContent, "text/html"); - // if it is a full HTML document, return the document itself as the parent container - if (contentWithSvgsRemoved.match(/<\/html>/)) { - content.generatedByIdiomorph = true; - return content; - } else { - // otherwise return the html element as the parent container - let htmlElement = content.firstChild; - if (htmlElement) { - htmlElement.generatedByIdiomorph = true; - return htmlElement; - } else { - return null; - } - } - } else { - // if it is partial HTML, wrap it in a template tag to provide a parent element and also to help - // deal with touchy tags like tr, tbody, etc. - let responseDoc = parser.parseFromString("", "text/html"); - let content = responseDoc.body.querySelector('template').content; - content.generatedByIdiomorph = true; - return content - } - } - - function normalizeContent(newContent) { - if (newContent == null) { - // noinspection UnnecessaryLocalVariableJS - const dummyParent = document.createElement('div'); - return dummyParent; - } else if (newContent.generatedByIdiomorph) { - // the template tag created by idiomorph parsing can serve as a dummy parent - return newContent; - } else if (newContent instanceof Node) { - // a single node is added as a child to a dummy parent - const dummyParent = document.createElement('div'); - dummyParent.append(newContent); - return dummyParent; - } else { - // all nodes in the array or HTMLElement collection are consolidated under - // a single dummy parent element - const dummyParent = document.createElement('div'); - for (const elt of [...newContent]) { - dummyParent.append(elt); - } - return dummyParent; - } - } - - function insertSiblings(previousSibling, morphedNode, nextSibling) { - let stack = []; - let added = []; - while (previousSibling != null) { - stack.push(previousSibling); - previousSibling = previousSibling.previousSibling; - } - while (stack.length > 0) { - let node = stack.pop(); - added.push(node); // push added preceding siblings on in order and insert - morphedNode.parentElement.insertBefore(node, morphedNode); - } - added.push(morphedNode); - while (nextSibling != null) { - stack.push(nextSibling); - added.push(nextSibling); // here we are going in order, so push on as we scan, rather than add - nextSibling = nextSibling.nextSibling; - } - while (stack.length > 0) { - morphedNode.parentElement.insertBefore(stack.pop(), morphedNode.nextSibling); - } - return added; - } - - function findBestNodeMatch(newContent, oldNode, ctx) { - let currentElement; - currentElement = newContent.firstChild; - let bestElement = currentElement; - let score = 0; - while (currentElement) { - let newScore = scoreElement(currentElement, oldNode, ctx); - if (newScore > score) { - bestElement = currentElement; - score = newScore; - } - currentElement = currentElement.nextSibling; - } - return bestElement; - } - - function scoreElement(node1, node2, ctx) { - if (isSoftMatch(node1, node2)) { - return .5 + getIdIntersectionCount(ctx, node1, node2); - } - return 0; - } - - function removeNode(tempNode, ctx) { - removeIdsFromConsideration(ctx, tempNode); - if (ctx.callbacks.beforeNodeRemoved(tempNode) === false) return; - - tempNode.remove(); - ctx.callbacks.afterNodeRemoved(tempNode); - } - - //============================================================================= - // ID Set Functions - //============================================================================= - - function isIdInConsideration(ctx, id) { - return !ctx.deadIds.has(id); - } - - function idIsWithinNode(ctx, id, targetNode) { - let idSet = ctx.idMap.get(targetNode) || EMPTY_SET; - return idSet.has(id); - } - - function removeIdsFromConsideration(ctx, node) { - let idSet = ctx.idMap.get(node) || EMPTY_SET; - for (const id of idSet) { - ctx.deadIds.add(id); - } - } - - function getIdIntersectionCount(ctx, node1, node2) { - let sourceSet = ctx.idMap.get(node1) || EMPTY_SET; - let matchCount = 0; - for (const id of sourceSet) { - // a potential match is an id in the source and potentialIdsSet, but - // that has not already been merged into the DOM - if (isIdInConsideration(ctx, id) && idIsWithinNode(ctx, id, node2)) { - ++matchCount; - } - } - return matchCount; - } - - /** - * A bottom up algorithm that finds all elements with ids inside of the node - * argument and populates id sets for those nodes and all their parents, generating - * a set of ids contained within all nodes for the entire hierarchy in the DOM - * - * @param node {Element} - * @param {Map>} idMap - */ - function populateIdMapForNode(node, idMap) { - let nodeParent = node.parentElement; - // find all elements with an id property - let idElements = node.querySelectorAll('[id]'); - for (const elt of idElements) { - let current = elt; - // walk up the parent hierarchy of that element, adding the id - // of element to the parent's id set - while (current !== nodeParent && current != null) { - let idSet = idMap.get(current); - // if the id set doesn't exist, create it and insert it in the map - if (idSet == null) { - idSet = new Set(); - idMap.set(current, idSet); - } - idSet.add(elt.id); - current = current.parentElement; - } - } - } - - /** - * This function computes a map of nodes to all ids contained within that node (inclusive of the - * node). This map can be used to ask if two nodes have intersecting sets of ids, which allows - * for a looser definition of "matching" than tradition id matching, and allows child nodes - * to contribute to a parent nodes matching. - * - * @param {Element} oldContent the old content that will be morphed - * @param {Element} newContent the new content to morph to - * @returns {Map>} a map of nodes to id sets for the - */ - function createIdMap(oldContent, newContent) { - let idMap = new Map(); - populateIdMapForNode(oldContent, idMap); - populateIdMapForNode(newContent, idMap); - return idMap; - } - - //============================================================================= - // This is what ends up becoming the Idiomorph global object - //============================================================================= - return { - morph, - defaults - } - })(); - -function normalizeAttributesForComparison(element) { - const isFileInput = element instanceof HTMLInputElement && element.type === 'file'; - if (!isFileInput) { - if ('value' in element) { - element.setAttribute('value', element.value); - } - else if (element.hasAttribute('value')) { - element.setAttribute('value', ''); - } - } - Array.from(element.children).forEach((child) => { - normalizeAttributesForComparison(child); - }); -} - -const syncAttributes = (fromEl, toEl) => { - for (let i = 0; i < fromEl.attributes.length; i++) { - const attr = fromEl.attributes[i]; - toEl.setAttribute(attr.name, attr.value); - } -}; -function executeMorphdom(rootFromElement, rootToElement, modifiedFieldElements, getElementValue, externalMutationTracker) { - const originalElementIdsToSwapAfter = []; - const originalElementsToPreserve = new Map(); - const markElementAsNeedingPostMorphSwap = (id, replaceWithClone) => { - const oldElement = originalElementsToPreserve.get(id); - if (!(oldElement instanceof HTMLElement)) { - throw new Error(`Original element with id ${id} not found`); - } - originalElementIdsToSwapAfter.push(id); - if (!replaceWithClone) { - return null; - } - const clonedOldElement = cloneHTMLElement(oldElement); - oldElement.replaceWith(clonedOldElement); - return clonedOldElement; - }; - rootToElement.querySelectorAll('[data-live-preserve]').forEach((newElement) => { - const id = newElement.id; - if (!id) { - throw new Error('The data-live-preserve attribute requires an id attribute to be set on the element'); - } - const oldElement = rootFromElement.querySelector(`#${id}`); - if (!(oldElement instanceof HTMLElement)) { - throw new Error(`The element with id "${id}" was not found in the original HTML`); - } - newElement.removeAttribute('data-live-preserve'); - originalElementsToPreserve.set(id, oldElement); - syncAttributes(newElement, oldElement); - }); - Idiomorph.morph(rootFromElement, rootToElement, { - callbacks: { - beforeNodeMorphed: (fromEl, toEl) => { - if (!(fromEl instanceof Element) || !(toEl instanceof Element)) { - return true; - } - if (fromEl === rootFromElement) { - return true; - } - if (fromEl.id && originalElementsToPreserve.has(fromEl.id)) { - if (fromEl.id === toEl.id) { - return false; - } - const clonedFromEl = markElementAsNeedingPostMorphSwap(fromEl.id, true); - if (!clonedFromEl) { - throw new Error('missing clone'); - } - Idiomorph.morph(clonedFromEl, toEl); - return false; - } - if (fromEl instanceof HTMLElement && toEl instanceof HTMLElement) { - if (typeof fromEl.__x !== 'undefined') { - if (!window.Alpine) { - throw new Error('Unable to access Alpine.js though the global window.Alpine variable. Please make sure Alpine.js is loaded before Symfony UX LiveComponent.'); - } - if (typeof window.Alpine.morph !== 'function') { - throw new Error('Unable to access Alpine.js morph function. Please make sure the Alpine.js Morph plugin is installed and loaded, see https://alpinejs.dev/plugins/morph for more information.'); - } - window.Alpine.morph(fromEl.__x, toEl); - } - if (externalMutationTracker.wasElementAdded(fromEl)) { - fromEl.insertAdjacentElement('afterend', toEl); - return false; - } - if (modifiedFieldElements.includes(fromEl)) { - setValueOnElement(toEl, getElementValue(fromEl)); - } - if (fromEl === document.activeElement && - fromEl !== document.body && - null !== getModelDirectiveFromElement(fromEl, false)) { - setValueOnElement(toEl, getElementValue(fromEl)); - } - const elementChanges = externalMutationTracker.getChangedElement(fromEl); - if (elementChanges) { - elementChanges.applyToElement(toEl); - } - if (fromEl.nodeName.toUpperCase() !== 'OPTION' && fromEl.isEqualNode(toEl)) { - const normalizedFromEl = cloneHTMLElement(fromEl); - normalizeAttributesForComparison(normalizedFromEl); - const normalizedToEl = cloneHTMLElement(toEl); - normalizeAttributesForComparison(normalizedToEl); - if (normalizedFromEl.isEqualNode(normalizedToEl)) { - return false; - } - } - } - if (fromEl.hasAttribute('data-skip-morph') || (fromEl.id && fromEl.id !== toEl.id)) { - fromEl.innerHTML = toEl.innerHTML; - return true; - } - if (fromEl.parentElement?.hasAttribute('data-skip-morph')) { - return false; - } - return !fromEl.hasAttribute('data-live-ignore'); - }, - beforeNodeRemoved(node) { - if (!(node instanceof HTMLElement)) { - return true; - } - if (node.id && originalElementsToPreserve.has(node.id)) { - markElementAsNeedingPostMorphSwap(node.id, false); - return true; - } - if (externalMutationTracker.wasElementAdded(node)) { - return false; - } - return !node.hasAttribute('data-live-ignore'); - }, - }, - }); - originalElementIdsToSwapAfter.forEach((id) => { - const newElement = rootFromElement.querySelector(`#${id}`); - const originalElement = originalElementsToPreserve.get(id); - if (!(newElement instanceof HTMLElement) || !(originalElement instanceof HTMLElement)) { - throw new Error('Missing elements.'); - } - newElement.replaceWith(originalElement); - }); -} - -class ChangingItemsTracker { - constructor() { - this.changedItems = new Map(); - this.removedItems = new Map(); - } - setItem(itemName, newValue, previousValue) { - if (this.removedItems.has(itemName)) { - const removedRecord = this.removedItems.get(itemName); - this.removedItems.delete(itemName); - if (removedRecord.original === newValue) { - return; - } - } - if (this.changedItems.has(itemName)) { - const originalRecord = this.changedItems.get(itemName); - if (originalRecord.original === newValue) { - this.changedItems.delete(itemName); - return; - } - this.changedItems.set(itemName, { original: originalRecord.original, new: newValue }); - return; - } - this.changedItems.set(itemName, { original: previousValue, new: newValue }); - } - removeItem(itemName, currentValue) { - let trueOriginalValue = currentValue; - if (this.changedItems.has(itemName)) { - const originalRecord = this.changedItems.get(itemName); - trueOriginalValue = originalRecord.original; - this.changedItems.delete(itemName); - if (trueOriginalValue === null) { - return; - } - } - if (!this.removedItems.has(itemName)) { - this.removedItems.set(itemName, { original: trueOriginalValue }); - } - } - getChangedItems() { - return Array.from(this.changedItems, ([name, { new: value }]) => ({ name, value })); - } - getRemovedItems() { - return Array.from(this.removedItems.keys()); - } - isEmpty() { - return this.changedItems.size === 0 && this.removedItems.size === 0; - } -} - -class ElementChanges { - constructor() { - this.addedClasses = new Set(); - this.removedClasses = new Set(); - this.styleChanges = new ChangingItemsTracker(); - this.attributeChanges = new ChangingItemsTracker(); - } - addClass(className) { - if (!this.removedClasses.delete(className)) { - this.addedClasses.add(className); - } - } - removeClass(className) { - if (!this.addedClasses.delete(className)) { - this.removedClasses.add(className); - } - } - addStyle(styleName, newValue, originalValue) { - this.styleChanges.setItem(styleName, newValue, originalValue); - } - removeStyle(styleName, originalValue) { - this.styleChanges.removeItem(styleName, originalValue); - } - addAttribute(attributeName, newValue, originalValue) { - this.attributeChanges.setItem(attributeName, newValue, originalValue); - } - removeAttribute(attributeName, originalValue) { - this.attributeChanges.removeItem(attributeName, originalValue); - } - getAddedClasses() { - return [...this.addedClasses]; - } - getRemovedClasses() { - return [...this.removedClasses]; - } - getChangedStyles() { - return this.styleChanges.getChangedItems(); - } - getRemovedStyles() { - return this.styleChanges.getRemovedItems(); - } - getChangedAttributes() { - return this.attributeChanges.getChangedItems(); - } - getRemovedAttributes() { - return this.attributeChanges.getRemovedItems(); - } - applyToElement(element) { - element.classList.add(...this.addedClasses); - element.classList.remove(...this.removedClasses); - this.styleChanges.getChangedItems().forEach((change) => { - element.style.setProperty(change.name, change.value); - return; - }); - this.styleChanges.getRemovedItems().forEach((styleName) => { - element.style.removeProperty(styleName); - }); - this.attributeChanges.getChangedItems().forEach((change) => { - element.setAttribute(change.name, change.value); - }); - this.attributeChanges.getRemovedItems().forEach((attributeName) => { - element.removeAttribute(attributeName); - }); - } - isEmpty() { - return (this.addedClasses.size === 0 && - this.removedClasses.size === 0 && - this.styleChanges.isEmpty() && - this.attributeChanges.isEmpty()); - } -} - -class ExternalMutationTracker { - constructor(element, shouldTrackChangeCallback) { - this.changedElements = new WeakMap(); - this.changedElementsCount = 0; - this.addedElements = []; - this.removedElements = []; - this.isStarted = false; - this.element = element; - this.shouldTrackChangeCallback = shouldTrackChangeCallback; - this.mutationObserver = new MutationObserver(this.onMutations.bind(this)); - } - start() { - if (this.isStarted) { - return; - } - this.mutationObserver.observe(this.element, { - childList: true, - subtree: true, - attributes: true, - attributeOldValue: true, - }); - this.isStarted = true; - } - stop() { - if (this.isStarted) { - this.mutationObserver.disconnect(); - this.isStarted = false; - } - } - getChangedElement(element) { - return this.changedElements.has(element) ? this.changedElements.get(element) : null; - } - getAddedElements() { - return this.addedElements; - } - wasElementAdded(element) { - return this.addedElements.includes(element); - } - handlePendingChanges() { - this.onMutations(this.mutationObserver.takeRecords()); - } - onMutations(mutations) { - const handledAttributeMutations = new WeakMap(); - for (const mutation of mutations) { - const element = mutation.target; - if (!this.shouldTrackChangeCallback(element)) { - continue; - } - if (this.isElementAddedByTranslation(element)) { - continue; - } - let isChangeInAddedElement = false; - for (const addedElement of this.addedElements) { - if (addedElement.contains(element)) { - isChangeInAddedElement = true; - break; - } - } - if (isChangeInAddedElement) { - continue; - } - switch (mutation.type) { - case 'childList': - this.handleChildListMutation(mutation); - break; - case 'attributes': - if (!handledAttributeMutations.has(element)) { - handledAttributeMutations.set(element, []); - } - if (!handledAttributeMutations.get(element).includes(mutation.attributeName)) { - this.handleAttributeMutation(mutation); - handledAttributeMutations.set(element, [ - ...handledAttributeMutations.get(element), - mutation.attributeName, - ]); - } - break; - } - } - } - handleChildListMutation(mutation) { - mutation.addedNodes.forEach((node) => { - if (!(node instanceof Element)) { - return; - } - if (this.removedElements.includes(node)) { - this.removedElements.splice(this.removedElements.indexOf(node), 1); - return; - } - if (this.isElementAddedByTranslation(node)) { - return; - } - this.addedElements.push(node); - }); - mutation.removedNodes.forEach((node) => { - if (!(node instanceof Element)) { - return; - } - if (this.addedElements.includes(node)) { - this.addedElements.splice(this.addedElements.indexOf(node), 1); - return; - } - this.removedElements.push(node); - }); - } - handleAttributeMutation(mutation) { - const element = mutation.target; - if (!this.changedElements.has(element)) { - this.changedElements.set(element, new ElementChanges()); - this.changedElementsCount++; - } - const changedElement = this.changedElements.get(element); - switch (mutation.attributeName) { - case 'class': - this.handleClassAttributeMutation(mutation, changedElement); - break; - case 'style': - this.handleStyleAttributeMutation(mutation, changedElement); - break; - default: - this.handleGenericAttributeMutation(mutation, changedElement); - } - if (changedElement.isEmpty()) { - this.changedElements.delete(element); - this.changedElementsCount--; - } - } - handleClassAttributeMutation(mutation, elementChanges) { - const element = mutation.target; - const previousValue = mutation.oldValue || ''; - const previousValues = previousValue.match(/(\S+)/gu) || []; - const newValues = [].slice.call(element.classList); - const addedValues = newValues.filter((value) => !previousValues.includes(value)); - const removedValues = previousValues.filter((value) => !newValues.includes(value)); - addedValues.forEach((value) => { - elementChanges.addClass(value); - }); - removedValues.forEach((value) => { - elementChanges.removeClass(value); - }); - } - handleStyleAttributeMutation(mutation, elementChanges) { - const element = mutation.target; - const previousValue = mutation.oldValue || ''; - const previousStyles = this.extractStyles(previousValue); - const newValue = element.getAttribute('style') || ''; - const newStyles = this.extractStyles(newValue); - const addedOrChangedStyles = Object.keys(newStyles).filter((key) => previousStyles[key] === undefined || previousStyles[key] !== newStyles[key]); - const removedStyles = Object.keys(previousStyles).filter((key) => !newStyles[key]); - addedOrChangedStyles.forEach((style) => { - elementChanges.addStyle(style, newStyles[style], previousStyles[style] === undefined ? null : previousStyles[style]); - }); - removedStyles.forEach((style) => { - elementChanges.removeStyle(style, previousStyles[style]); - }); - } - handleGenericAttributeMutation(mutation, elementChanges) { - const attributeName = mutation.attributeName; - const element = mutation.target; - let oldValue = mutation.oldValue; - let newValue = element.getAttribute(attributeName); - if (oldValue === attributeName) { - oldValue = ''; - } - if (newValue === attributeName) { - newValue = ''; - } - if (!element.hasAttribute(attributeName)) { - if (oldValue === null) { - return; - } - elementChanges.removeAttribute(attributeName, mutation.oldValue); - return; - } - if (newValue === oldValue) { - return; - } - elementChanges.addAttribute(attributeName, element.getAttribute(attributeName), mutation.oldValue); - } - extractStyles(styles) { - const styleObject = {}; - styles.split(';').forEach((style) => { - const parts = style.split(':'); - if (parts.length === 1) { - return; - } - const property = parts[0].trim(); - styleObject[property] = parts.slice(1).join(':').trim(); - }); - return styleObject; - } - isElementAddedByTranslation(element) { - return element.tagName === 'FONT' && element.getAttribute('style') === 'vertical-align: inherit;'; - } -} - -class UnsyncedInputsTracker { - constructor(component, modelElementResolver) { - this.elementEventListeners = [ - { event: 'input', callback: (event) => this.handleInputEvent(event) }, - ]; - this.component = component; - this.modelElementResolver = modelElementResolver; - this.unsyncedInputs = new UnsyncedInputContainer(); - } - activate() { - this.elementEventListeners.forEach(({ event, callback }) => { - this.component.element.addEventListener(event, callback); - }); - } - deactivate() { - this.elementEventListeners.forEach(({ event, callback }) => { - this.component.element.removeEventListener(event, callback); - }); - } - markModelAsSynced(modelName) { - this.unsyncedInputs.markModelAsSynced(modelName); - } - handleInputEvent(event) { - const target = event.target; - if (!target) { - return; - } - this.updateModelFromElement(target); - } - updateModelFromElement(element) { - if (!elementBelongsToThisComponent(element, this.component)) { - return; - } - if (!(element instanceof HTMLElement)) { - throw new Error('Could not update model for non HTMLElement'); - } - const modelName = this.modelElementResolver.getModelName(element); - this.unsyncedInputs.add(element, modelName); - } - getUnsyncedInputs() { - return this.unsyncedInputs.allUnsyncedInputs(); - } - getUnsyncedModels() { - return Array.from(this.unsyncedInputs.getUnsyncedModelNames()); - } - resetUnsyncedFields() { - this.unsyncedInputs.resetUnsyncedFields(); - } -} -class UnsyncedInputContainer { - constructor() { - this.unsyncedNonModelFields = []; - this.unsyncedModelNames = []; - this.unsyncedModelFields = new Map(); - } - add(element, modelName = null) { - if (modelName) { - this.unsyncedModelFields.set(modelName, element); - if (!this.unsyncedModelNames.includes(modelName)) { - this.unsyncedModelNames.push(modelName); - } - return; - } - this.unsyncedNonModelFields.push(element); - } - resetUnsyncedFields() { - this.unsyncedModelFields.forEach((value, key) => { - if (!this.unsyncedModelNames.includes(key)) { - this.unsyncedModelFields.delete(key); - } - }); - } - allUnsyncedInputs() { - return [...this.unsyncedNonModelFields, ...this.unsyncedModelFields.values()]; - } - markModelAsSynced(modelName) { - const index = this.unsyncedModelNames.indexOf(modelName); - if (index !== -1) { - this.unsyncedModelNames.splice(index, 1); - } - } - getUnsyncedModelNames() { - return this.unsyncedModelNames; - } -} - -function getDeepData(data, propertyPath) { - const { currentLevelData, finalKey } = parseDeepData(data, propertyPath); - if (currentLevelData === undefined) { - return undefined; - } - return currentLevelData[finalKey]; -} -const parseDeepData = (data, propertyPath) => { - const finalData = JSON.parse(JSON.stringify(data)); - let currentLevelData = finalData; - const parts = propertyPath.split('.'); - for (let i = 0; i < parts.length - 1; i++) { - currentLevelData = currentLevelData[parts[i]]; - } - const finalKey = parts[parts.length - 1]; - return { - currentLevelData, - finalData, - finalKey, - parts, - }; -}; - -class ValueStore { - constructor(props) { - this.props = {}; - this.dirtyProps = {}; - this.pendingProps = {}; - this.updatedPropsFromParent = {}; - this.props = props; - } - get(name) { - const normalizedName = normalizeModelName(name); - if (this.dirtyProps[normalizedName] !== undefined) { - return this.dirtyProps[normalizedName]; - } - if (this.pendingProps[normalizedName] !== undefined) { - return this.pendingProps[normalizedName]; - } - if (this.props[normalizedName] !== undefined) { - return this.props[normalizedName]; - } - return getDeepData(this.props, normalizedName); - } - has(name) { - return this.get(name) !== undefined; - } - set(name, value) { - const normalizedName = normalizeModelName(name); - if (this.get(normalizedName) === value) { - return false; - } - this.dirtyProps[normalizedName] = value; - return true; - } - getOriginalProps() { - return { ...this.props }; - } - getDirtyProps() { - return { ...this.dirtyProps }; - } - getUpdatedPropsFromParent() { - return { ...this.updatedPropsFromParent }; - } - flushDirtyPropsToPending() { - this.pendingProps = { ...this.dirtyProps }; - this.dirtyProps = {}; - } - reinitializeAllProps(props) { - this.props = props; - this.updatedPropsFromParent = {}; - this.pendingProps = {}; - } - pushPendingPropsBackToDirty() { - this.dirtyProps = { ...this.pendingProps, ...this.dirtyProps }; - this.pendingProps = {}; - } - storeNewPropsFromParent(props) { - let changed = false; - for (const [key, value] of Object.entries(props)) { - const currentValue = this.get(key); - if (currentValue !== value) { - changed = true; - } - } - if (changed) { - this.updatedPropsFromParent = props; - } - return changed; - } -} - -class Component { - constructor(element, name, props, listeners, id, backend, elementDriver) { - this.fingerprint = ''; - this.defaultDebounce = 150; - this.backendRequest = null; - this.pendingActions = []; - this.pendingFiles = {}; - this.isRequestPending = false; - this.requestDebounceTimeout = null; - this.element = element; - this.name = name; - this.backend = backend; - this.elementDriver = elementDriver; - this.id = id; - this.listeners = new Map(); - listeners.forEach((listener) => { - if (!this.listeners.has(listener.event)) { - this.listeners.set(listener.event, []); - } - this.listeners.get(listener.event)?.push(listener.action); - }); - this.valueStore = new ValueStore(props); - this.unsyncedInputsTracker = new UnsyncedInputsTracker(this, elementDriver); - this.hooks = new HookManager(); - this.resetPromise(); - this.externalMutationTracker = new ExternalMutationTracker(this.element, (element) => elementBelongsToThisComponent(element, this)); - this.externalMutationTracker.start(); - } - addPlugin(plugin) { - plugin.attachToComponent(this); - } - connect() { - registerComponent(this); - this.hooks.triggerHook('connect', this); - this.unsyncedInputsTracker.activate(); - this.externalMutationTracker.start(); - } - disconnect() { - unregisterComponent(this); - this.hooks.triggerHook('disconnect', this); - this.clearRequestDebounceTimeout(); - this.unsyncedInputsTracker.deactivate(); - this.externalMutationTracker.stop(); - } - on(hookName, callback) { - this.hooks.register(hookName, callback); - } - off(hookName, callback) { - this.hooks.unregister(hookName, callback); - } - set(model, value, reRender = false, debounce = false) { - const promise = this.nextRequestPromise; - const modelName = normalizeModelName(model); - if (!this.valueStore.has(modelName)) { - throw new Error(`Invalid model name "${model}".`); - } - const isChanged = this.valueStore.set(modelName, value); - this.hooks.triggerHook('model:set', model, value, this); - this.unsyncedInputsTracker.markModelAsSynced(modelName); - if (reRender && isChanged) { - this.debouncedStartRequest(debounce); - } - return promise; - } - getData(model) { - const modelName = normalizeModelName(model); - if (!this.valueStore.has(modelName)) { - throw new Error(`Invalid model "${model}".`); - } - return this.valueStore.get(modelName); - } - action(name, args = {}, debounce = false) { - const promise = this.nextRequestPromise; - this.pendingActions.push({ - name, - args, - }); - this.debouncedStartRequest(debounce); - return promise; - } - files(key, input) { - this.pendingFiles[key] = input; - } - render() { - const promise = this.nextRequestPromise; - this.tryStartingRequest(); - return promise; - } - getUnsyncedModels() { - return this.unsyncedInputsTracker.getUnsyncedModels(); - } - emit(name, data, onlyMatchingComponentsNamed = null) { - this.performEmit(name, data, false, onlyMatchingComponentsNamed); - } - emitUp(name, data, onlyMatchingComponentsNamed = null) { - this.performEmit(name, data, true, onlyMatchingComponentsNamed); - } - emitSelf(name, data) { - this.doEmit(name, data); - } - performEmit(name, data, emitUp, matchingName) { - const components = findComponents(this, emitUp, matchingName); - components.forEach((component) => { - component.doEmit(name, data); - }); - } - doEmit(name, data) { - if (!this.listeners.has(name)) { - return; - } - const actions = this.listeners.get(name) || []; - actions.forEach((action) => { - this.action(action, data, 1); - }); - } - isTurboEnabled() { - return typeof Turbo !== 'undefined' && !this.element.closest('[data-turbo="false"]'); - } - tryStartingRequest() { - if (!this.backendRequest) { - this.performRequest(); - return; - } - this.isRequestPending = true; - } - performRequest() { - const thisPromiseResolve = this.nextRequestPromiseResolve; - this.resetPromise(); - this.unsyncedInputsTracker.resetUnsyncedFields(); - const filesToSend = {}; - for (const [key, value] of Object.entries(this.pendingFiles)) { - if (value.files) { - filesToSend[key] = value.files; - } - } - const requestConfig = { - props: this.valueStore.getOriginalProps(), - actions: this.pendingActions, - updated: this.valueStore.getDirtyProps(), - children: {}, - updatedPropsFromParent: this.valueStore.getUpdatedPropsFromParent(), - files: filesToSend, - }; - this.hooks.triggerHook('request:started', requestConfig); - this.backendRequest = this.backend.makeRequest(requestConfig.props, requestConfig.actions, requestConfig.updated, requestConfig.children, requestConfig.updatedPropsFromParent, requestConfig.files); - this.hooks.triggerHook('loading.state:started', this.element, this.backendRequest); - this.pendingActions = []; - this.valueStore.flushDirtyPropsToPending(); - this.isRequestPending = false; - this.backendRequest.promise.then(async (response) => { - const backendResponse = new BackendResponse(response); - const html = await backendResponse.getBody(); - for (const input of Object.values(this.pendingFiles)) { - input.value = ''; - } - const headers = backendResponse.response.headers; - if (!headers.get('Content-Type')?.includes('application/vnd.live-component+html') && - !headers.get('X-Live-Redirect')) { - const controls = { displayError: true }; - this.valueStore.pushPendingPropsBackToDirty(); - this.hooks.triggerHook('response:error', backendResponse, controls); - if (controls.displayError) { - this.renderError(html); - } - this.backendRequest = null; - thisPromiseResolve(backendResponse); - return response; - } - this.processRerender(html, backendResponse); - const liveUrl = backendResponse.getLiveUrl(); - if (liveUrl) { - history.replaceState(history.state, '', new URL(liveUrl + window.location.hash, window.location.origin)); - } - this.backendRequest = null; - thisPromiseResolve(backendResponse); - if (this.isRequestPending) { - this.isRequestPending = false; - this.performRequest(); - } - return response; - }); - } - processRerender(html, backendResponse) { - const controls = { shouldRender: true }; - this.hooks.triggerHook('render:started', html, backendResponse, controls); - if (!controls.shouldRender) { - return; - } - if (backendResponse.response.headers.get('Location')) { - if (this.isTurboEnabled()) { - Turbo.visit(backendResponse.response.headers.get('Location')); - } - else { - window.location.href = backendResponse.response.headers.get('Location') || ''; - } - return; - } - this.hooks.triggerHook('loading.state:finished', this.element); - const modifiedModelValues = {}; - Object.keys(this.valueStore.getDirtyProps()).forEach((modelName) => { - modifiedModelValues[modelName] = this.valueStore.get(modelName); - }); - let newElement; - try { - newElement = htmlToElement(html); - if (!newElement.matches('[data-controller~=live]')) { - throw new Error('A live component template must contain a single root controller element.'); - } - } - catch (error) { - console.error(`There was a problem with the '${this.name}' component HTML returned:`, { - id: this.id, - }); - throw error; - } - this.externalMutationTracker.handlePendingChanges(); - this.externalMutationTracker.stop(); - executeMorphdom(this.element, newElement, this.unsyncedInputsTracker.getUnsyncedInputs(), (element) => getValueFromElement(element, this.valueStore), this.externalMutationTracker); - this.externalMutationTracker.start(); - const newProps = this.elementDriver.getComponentProps(); - this.valueStore.reinitializeAllProps(newProps); - const eventsToEmit = this.elementDriver.getEventsToEmit(); - const browserEventsToDispatch = this.elementDriver.getBrowserEventsToDispatch(); - Object.keys(modifiedModelValues).forEach((modelName) => { - this.valueStore.set(modelName, modifiedModelValues[modelName]); - }); - eventsToEmit.forEach(({ event, data, target, componentName }) => { - if (target === 'up') { - this.emitUp(event, data, componentName); - return; - } - if (target === 'self') { - this.emitSelf(event, data); - return; - } - this.emit(event, data, componentName); - }); - browserEventsToDispatch.forEach(({ event, payload }) => { - this.element.dispatchEvent(new CustomEvent(event, { - detail: payload, - bubbles: true, - })); - }); - this.hooks.triggerHook('render:finished', this); - } - calculateDebounce(debounce) { - if (debounce === true) { - return this.defaultDebounce; - } - if (debounce === false) { - return 0; - } - return debounce; - } - clearRequestDebounceTimeout() { - if (this.requestDebounceTimeout) { - clearTimeout(this.requestDebounceTimeout); - this.requestDebounceTimeout = null; - } - } - debouncedStartRequest(debounce) { - this.clearRequestDebounceTimeout(); - this.requestDebounceTimeout = window.setTimeout(() => { - this.render(); - }, this.calculateDebounce(debounce)); - } - renderError(html) { - let modal = document.getElementById('live-component-error'); - if (modal) { - modal.innerHTML = ''; - } - else { - modal = document.createElement('div'); - modal.id = 'live-component-error'; - modal.style.padding = '50px'; - modal.style.backgroundColor = 'rgba(0, 0, 0, .5)'; - modal.style.zIndex = '100000'; - modal.style.position = 'fixed'; - modal.style.top = '0px'; - modal.style.bottom = '0px'; - modal.style.left = '0px'; - modal.style.right = '0px'; - modal.style.display = 'flex'; - modal.style.flexDirection = 'column'; - } - const iframe = document.createElement('iframe'); - iframe.style.borderRadius = '5px'; - iframe.style.flexGrow = '1'; - modal.appendChild(iframe); - document.body.prepend(modal); - document.body.style.overflow = 'hidden'; - if (iframe.contentWindow) { - iframe.contentWindow.document.open(); - iframe.contentWindow.document.write(html); - iframe.contentWindow.document.close(); - } - const closeModal = (modal) => { - if (modal) { - modal.outerHTML = ''; - } - document.body.style.overflow = 'visible'; - }; - modal.addEventListener('click', () => closeModal(modal)); - modal.setAttribute('tabindex', '0'); - modal.addEventListener('keydown', (e) => { - if (e.key === 'Escape') { - closeModal(modal); - } - }); - modal.focus(); - } - resetPromise() { - this.nextRequestPromise = new Promise((resolve) => { - this.nextRequestPromiseResolve = resolve; - }); - } - _updateFromParentProps(props) { - const isChanged = this.valueStore.storeNewPropsFromParent(props); - if (isChanged) { - this.render(); - } - } -} -function proxifyComponent(component) { - return new Proxy(component, { - get(component, prop) { - if (prop in component || typeof prop !== 'string') { - if (typeof component[prop] === 'function') { - const callable = component[prop]; - return (...args) => { - return callable.apply(component, args); - }; - } - return Reflect.get(component, prop); - } - if (component.valueStore.has(prop)) { - return component.getData(prop); - } - return (args) => { - return component.action.apply(component, [prop, args]); - }; - }, - set(target, property, value) { - if (property in target) { - target[property] = value; - return true; - } - target.set(property, value); - return true; - }, +// src/live_controller.ts +var _LiveControllerDefault = class _LiveControllerDefault extends Controller { + constructor() { + super(...arguments); + this.pendingActionTriggerModelElement = null; + this.elementEventListeners = [ + { event: "input", callback: (event) => this.handleInputEvent(event) }, + { event: "change", callback: (event) => this.handleChangeEvent(event) } + ]; + this.pendingFiles = {}; + } + initialize() { + this.mutationObserver = new MutationObserver(this.onMutations.bind(this)); + this.createComponent(); + } + connect() { + this.connectComponent(); + this.mutationObserver.observe(this.element, { + attributes: true }); -} - -class StimulusElementDriver { - constructor(controller) { - this.controller = controller; - } - getModelName(element) { - const modelDirective = getModelDirectiveFromElement(element, false); - if (!modelDirective) { - return null; - } - return modelDirective.action; - } - getComponentProps() { - return this.controller.propsValue; - } - getEventsToEmit() { - return this.controller.eventsToEmitValue; - } - getBrowserEventsToDispatch() { - return this.controller.eventsToDispatchValue; - } -} - -function getModelBinding (modelDirective) { - let shouldRender = true; - let targetEventName = null; + } + disconnect() { + this.disconnectComponent(); + this.mutationObserver.disconnect(); + } + /** + * Called to update one piece of the model. + * + *