- Delete old Vite+Svelte frontend - Initialize new SvelteKit project with TypeScript - Configure Tailwind CSS v4 + DaisyUI - Implement JWT authentication with auto-refresh - Create login page with form validation (Zod) - Add protected route guards - Update Docker configuration for single-stage build - Add E2E tests with Playwright (6/11 passing) - Fix Svelte 5 reactivity with $state() runes Known issues: - 5 E2E tests failing (timing/async issues) - Token refresh implementation needs debugging - Validation error display timing
97 lines
3.4 KiB
JavaScript
97 lines
3.4 KiB
JavaScript
import { createRule } from '../utils/index.js';
|
|
import { getSvelteVersion } from '../utils/svelte-context.js';
|
|
const EXPECTED_PROP_NAMES = ['data', 'errors', 'form', 'params', 'snapshot'];
|
|
const EXPECTED_PROP_NAMES_SVELTE5 = [...EXPECTED_PROP_NAMES, 'children'];
|
|
function checkProp(node, context, expectedPropNames) {
|
|
if (node.id.type !== 'ObjectPattern')
|
|
return;
|
|
for (const p of node.id.properties) {
|
|
if (p.type === 'Property' &&
|
|
p.key.type === 'Identifier' &&
|
|
!expectedPropNames.includes(p.key.name)) {
|
|
context.report({
|
|
node: p.key,
|
|
loc: p.key.loc,
|
|
messageId: 'unexpected'
|
|
});
|
|
}
|
|
}
|
|
}
|
|
function isModuleScript(node) {
|
|
// <script context="module">
|
|
if (node.key.name === 'context' &&
|
|
node.value.some((v) => v.type === 'SvelteLiteral' && v.value === 'module')) {
|
|
return true;
|
|
}
|
|
// <script module>
|
|
if (node.key.name === 'module' && node.value.length === 0) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
export default createRule('valid-prop-names-in-kit-pages', {
|
|
meta: {
|
|
docs: {
|
|
description: 'disallow props other than data or errors in SvelteKit page components.',
|
|
category: 'SvelteKit',
|
|
recommended: true
|
|
},
|
|
schema: [],
|
|
messages: {
|
|
unexpected: 'disallow props other than data or errors in SvelteKit page components.'
|
|
},
|
|
type: 'problem',
|
|
conditions: [
|
|
{
|
|
svelteKitFileTypes: ['+page.svelte', '+error.svelte', '+layout.svelte']
|
|
}
|
|
]
|
|
},
|
|
create(context) {
|
|
let isScript = false;
|
|
const isSvelte5 = getSvelteVersion() === '5';
|
|
const expectedPropNames = isSvelte5 ? EXPECTED_PROP_NAMES_SVELTE5 : EXPECTED_PROP_NAMES;
|
|
return {
|
|
// <script>
|
|
'Program > SvelteScriptElement > SvelteStartTag': (node) => {
|
|
// except for <script context="module">
|
|
isScript = !node.attributes.some((a) => a.type === 'SvelteAttribute' && isModuleScript(a));
|
|
},
|
|
// </script>
|
|
'Program > SvelteScriptElement:exit': () => {
|
|
isScript = false;
|
|
},
|
|
// Svelte3,4
|
|
'ExportNamedDeclaration > VariableDeclaration > VariableDeclarator': (node) => {
|
|
if (!isScript)
|
|
return;
|
|
// export let foo
|
|
if (node.id.type === 'Identifier') {
|
|
if (!expectedPropNames.includes(node.id.name)) {
|
|
context.report({
|
|
node,
|
|
loc: node.loc,
|
|
messageId: 'unexpected'
|
|
});
|
|
}
|
|
return;
|
|
}
|
|
// export let { xxx, yyy } = zzz
|
|
checkProp(node, context, expectedPropNames);
|
|
},
|
|
// Svelte5
|
|
// let { foo, bar } = $props();
|
|
'VariableDeclaration > VariableDeclarator': (node) => {
|
|
if (!isScript)
|
|
return;
|
|
if (node.init?.type !== 'CallExpression' ||
|
|
node.init.callee?.type !== 'Identifier' ||
|
|
node.init.callee?.name !== '$props') {
|
|
return;
|
|
}
|
|
checkProp(node, context, expectedPropNames);
|
|
}
|
|
};
|
|
}
|
|
});
|