- 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
261 lines
8.5 KiB
JavaScript
261 lines
8.5 KiB
JavaScript
import fs from 'fs';
|
|
import path from 'path';
|
|
import { getPackageJsons } from './get-package-json.js';
|
|
import { getNodeModule } from './get-node-module.js';
|
|
import { createCache } from './cache.js';
|
|
import { VERSION as SVELTE_VERSION } from 'svelte/compiler';
|
|
const isRunInBrowser = !fs.readFileSync;
|
|
function getSvelteFileType(filePath) {
|
|
if (filePath.endsWith('.svelte')) {
|
|
return '.svelte';
|
|
}
|
|
if (filePath.endsWith('.svelte.js') || filePath.endsWith('.svelte.ts')) {
|
|
return '.svelte.[js|ts]';
|
|
}
|
|
return null;
|
|
}
|
|
function getSvelteKitFileTypeFromFilePath(filePath) {
|
|
const fileName = filePath.split(/[/\\]/).pop();
|
|
switch (fileName) {
|
|
case '+page.svelte': {
|
|
return '+page.svelte';
|
|
}
|
|
case '+page.js':
|
|
case '+page.ts': {
|
|
return '+page.[js|ts]';
|
|
}
|
|
case '+page.server.js':
|
|
case '+page.server.ts': {
|
|
return '+page.server.[js|ts]';
|
|
}
|
|
case '+error.svelte': {
|
|
return '+error.svelte';
|
|
}
|
|
case '+layout.svelte': {
|
|
return '+layout.svelte';
|
|
}
|
|
case '+layout.js':
|
|
case '+layout.ts': {
|
|
return '+layout.[js|ts]';
|
|
}
|
|
case '+layout.server.js':
|
|
case '+layout.server.ts': {
|
|
return '+layout.server.[js|ts]';
|
|
}
|
|
case '+server.js':
|
|
case '+server.ts': {
|
|
return '+server.[js|ts]';
|
|
}
|
|
default: {
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
function extractMajorVersion(version, recognizePrereleaseVersion) {
|
|
if (recognizePrereleaseVersion) {
|
|
const match = /^(?:\^|~)?(\d+\.0\.0-next)/.exec(version);
|
|
if (match && match[1]) {
|
|
return match[1];
|
|
}
|
|
}
|
|
const match = /^(?:\^|~)?(\d+)\./.exec(version);
|
|
if (match && match[1]) {
|
|
return match[1];
|
|
}
|
|
return null;
|
|
}
|
|
const svelteKitContextCache = createCache();
|
|
function getSvelteKitContext(context) {
|
|
const filePath = context.filename;
|
|
const cached = svelteKitContextCache.get(filePath);
|
|
if (cached)
|
|
return cached;
|
|
const svelteKitVersion = getSvelteKitVersion(filePath);
|
|
if (svelteKitVersion == null) {
|
|
const result = {
|
|
svelteKitFileType: null,
|
|
svelteKitVersion: null
|
|
};
|
|
svelteKitContextCache.set(filePath, result);
|
|
return result;
|
|
}
|
|
if (isRunInBrowser) {
|
|
const result = {
|
|
svelteKitVersion,
|
|
// Judge by only file path if it runs in browser.
|
|
svelteKitFileType: getSvelteKitFileTypeFromFilePath(filePath)
|
|
};
|
|
svelteKitContextCache.set(filePath, result);
|
|
return result;
|
|
}
|
|
const routes = (context.settings?.svelte?.kit?.files?.routes ??
|
|
context.sourceCode.parserServices.svelteParseContext?.svelteConfig?.kit?.files?.routes)?.replace(/^\//, '') ?? 'src/routes';
|
|
const projectRootDir = getProjectRootDir(context.filename) ?? '';
|
|
if (!filePath.startsWith(path.join(projectRootDir, routes))) {
|
|
const result = {
|
|
svelteKitVersion,
|
|
svelteKitFileType: null
|
|
};
|
|
svelteKitContextCache.set(filePath, result);
|
|
return result;
|
|
}
|
|
const result = {
|
|
svelteKitVersion,
|
|
svelteKitFileType: getSvelteKitFileTypeFromFilePath(filePath)
|
|
};
|
|
svelteKitContextCache.set(filePath, result);
|
|
return result;
|
|
}
|
|
function checkAndSetSvelteVersion(version) {
|
|
const major = extractMajorVersion(version, false);
|
|
if (major == null) {
|
|
return null;
|
|
}
|
|
if (major === '3' || major === '4') {
|
|
return '3/4';
|
|
}
|
|
return major;
|
|
}
|
|
export function getSvelteVersion() {
|
|
// Hack: if it runs in browser, it regards as Svelte project.
|
|
if (isRunInBrowser) {
|
|
return '5';
|
|
}
|
|
return checkAndSetSvelteVersion(SVELTE_VERSION);
|
|
}
|
|
const svelteKitVersionCache = createCache();
|
|
function checkAndSetSvelteKitVersion(version, filePath) {
|
|
const major = extractMajorVersion(version, true);
|
|
svelteKitVersionCache.set(filePath, major);
|
|
return major;
|
|
}
|
|
function getSvelteKitVersion(filePath) {
|
|
const cached = svelteKitVersionCache.get(filePath);
|
|
if (cached)
|
|
return cached;
|
|
// Hack: if it runs in browser, it regards as SvelteKit project.
|
|
if (isRunInBrowser) {
|
|
svelteKitVersionCache.set(filePath, '2');
|
|
return '2';
|
|
}
|
|
const nodeModule = getNodeModule('@sveltejs/kit', filePath);
|
|
if (nodeModule) {
|
|
try {
|
|
const packageJson = JSON.parse(fs.readFileSync(path.join(nodeModule, 'package.json'), 'utf8'));
|
|
const result = checkAndSetSvelteKitVersion(packageJson.version, filePath);
|
|
if (result != null) {
|
|
return result;
|
|
}
|
|
}
|
|
catch {
|
|
/** do nothing */
|
|
}
|
|
}
|
|
try {
|
|
const packageJsons = getPackageJsons(filePath);
|
|
if (packageJsons.length > 0) {
|
|
if (packageJsons[0].name === 'eslint-plugin-svelte') {
|
|
// Hack: CI removes `@sveltejs/kit` and it returns false and test failed.
|
|
// So always it returns 2 if it runs on the package.
|
|
svelteKitVersionCache.set(filePath, '2');
|
|
return '2';
|
|
}
|
|
for (const packageJson of packageJsons) {
|
|
const version = packageJson.dependencies?.['@sveltejs/kit'] ??
|
|
packageJson.devDependencies?.['@sveltejs/kit'];
|
|
if (typeof version === 'string') {
|
|
const result = checkAndSetSvelteKitVersion(version, filePath);
|
|
if (result != null) {
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch {
|
|
/** do nothing */
|
|
}
|
|
svelteKitVersionCache.set(filePath, null);
|
|
return null;
|
|
}
|
|
const projectRootDirCache = createCache();
|
|
/**
|
|
* Gets a project root folder path.
|
|
* @param filePath A file path to lookup.
|
|
* @returns A found project root folder path or null.
|
|
*/
|
|
function getProjectRootDir(filePath) {
|
|
if (isRunInBrowser)
|
|
return null;
|
|
const cached = projectRootDirCache.get(filePath);
|
|
if (cached)
|
|
return cached;
|
|
const packageJsons = getPackageJsons(filePath);
|
|
if (packageJsons.length === 0) {
|
|
projectRootDirCache.set(filePath, null);
|
|
return null;
|
|
}
|
|
const packageJsonFilePath = packageJsons[0].filePath;
|
|
if (!packageJsonFilePath) {
|
|
projectRootDirCache.set(filePath, null);
|
|
return null;
|
|
}
|
|
const projectRootDir = path.dirname(path.resolve(packageJsonFilePath));
|
|
projectRootDirCache.set(filePath, projectRootDir);
|
|
return projectRootDir;
|
|
}
|
|
const svelteContextCache = createCache();
|
|
export function getSvelteContext(context) {
|
|
const { parserServices } = context.sourceCode;
|
|
const { svelteParseContext } = parserServices;
|
|
const filePath = context.filename;
|
|
const cached = svelteContextCache.get(filePath);
|
|
if (cached)
|
|
return cached;
|
|
const svelteKitContext = getSvelteKitContext(context);
|
|
const svelteVersion = getSvelteVersion();
|
|
const svelteFileType = getSvelteFileType(filePath);
|
|
if (svelteVersion == null) {
|
|
const result = {
|
|
svelteVersion: null,
|
|
svelteFileType: null,
|
|
runes: null,
|
|
svelteKitVersion: svelteKitContext.svelteKitVersion,
|
|
svelteKitFileType: svelteKitContext.svelteKitFileType
|
|
};
|
|
svelteContextCache.set(filePath, result);
|
|
return result;
|
|
}
|
|
if (svelteVersion === '3/4') {
|
|
const result = {
|
|
svelteVersion,
|
|
svelteFileType: svelteFileType === '.svelte' ? '.svelte' : null,
|
|
runes: null,
|
|
svelteKitVersion: svelteKitContext.svelteKitVersion,
|
|
svelteKitFileType: svelteKitContext.svelteKitFileType
|
|
};
|
|
svelteContextCache.set(filePath, result);
|
|
return result;
|
|
}
|
|
if (svelteFileType == null) {
|
|
const result = {
|
|
svelteVersion,
|
|
svelteFileType: null,
|
|
runes: null,
|
|
svelteKitVersion: svelteKitContext.svelteKitVersion,
|
|
svelteKitFileType: svelteKitContext.svelteKitFileType
|
|
};
|
|
svelteContextCache.set(filePath, result);
|
|
return result;
|
|
}
|
|
const result = {
|
|
svelteVersion,
|
|
runes: svelteParseContext?.runes ?? 'undetermined',
|
|
svelteFileType,
|
|
svelteKitVersion: svelteKitContext.svelteKitVersion,
|
|
svelteKitFileType: svelteKitContext.svelteKitFileType
|
|
};
|
|
svelteContextCache.set(filePath, result);
|
|
return result;
|
|
}
|