- 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
221 lines
7.2 KiB
JavaScript
221 lines
7.2 KiB
JavaScript
import { isNotWhitespace } from './ast.js';
|
|
import { isBeginningOfLine } from './commons.js';
|
|
import { getFirstAndLastTokens } from './commons.js';
|
|
export class OffsetContext {
|
|
constructor(arg) {
|
|
this.offsets = new Map();
|
|
this.ignoreRanges = new Map();
|
|
this.sourceCode = arg.sourceCode;
|
|
this.options = arg.options;
|
|
}
|
|
/**
|
|
* Set offset to the given index.
|
|
*/
|
|
setOffsetIndex(index, offset, base) {
|
|
if (index === base) {
|
|
return;
|
|
}
|
|
this.offsets.set(index, {
|
|
type: 0 /* OffsetDataType.normal */,
|
|
base,
|
|
offset
|
|
});
|
|
}
|
|
/**
|
|
* Set align indent to the given index.
|
|
*/
|
|
setAlignIndent(index, alignIndent, base) {
|
|
if (index === base) {
|
|
return;
|
|
}
|
|
this.offsets.set(index, {
|
|
type: 1 /* OffsetDataType.align */,
|
|
base,
|
|
alignIndent
|
|
});
|
|
}
|
|
/**
|
|
* Set offset to the given tokens.
|
|
*/
|
|
setOffsetToken(token, offset, baseToken) {
|
|
if (!token) {
|
|
return;
|
|
}
|
|
if (Array.isArray(token)) {
|
|
for (const t of token) {
|
|
this.setOffsetToken(t, offset, baseToken);
|
|
}
|
|
return;
|
|
}
|
|
this.setOffsetIndex(token.range[0], offset, baseToken.range[0]);
|
|
}
|
|
/**
|
|
* Copy offset to the given index from srcIndex.
|
|
*/
|
|
copyOffset(index, srcIndex) {
|
|
const offsetData = this.offsets.get(srcIndex);
|
|
if (!offsetData) {
|
|
return;
|
|
}
|
|
if (offsetData.type === 2 /* OffsetDataType.start */) {
|
|
this.setStartOffsetIndex(index, offsetData.offset);
|
|
}
|
|
else if (offsetData.type === 1 /* OffsetDataType.align */) {
|
|
this.setAlignIndent(index, offsetData.alignIndent, offsetData.base);
|
|
}
|
|
else {
|
|
this.setOffsetIndex(index, offsetData.offset, offsetData.base);
|
|
}
|
|
}
|
|
/**
|
|
* Set start offset to the given index.
|
|
*/
|
|
setStartOffsetIndex(index, offset) {
|
|
this.offsets.set(index, {
|
|
type: 2 /* OffsetDataType.start */,
|
|
offset
|
|
});
|
|
}
|
|
/**
|
|
* Set start offset to the given token.
|
|
*/
|
|
setStartOffsetToken(token, offset) {
|
|
if (!token) {
|
|
return;
|
|
}
|
|
if (Array.isArray(token)) {
|
|
for (const t of token) {
|
|
this.setStartOffsetToken(t, offset);
|
|
}
|
|
return;
|
|
}
|
|
this.setStartOffsetIndex(token.range[0], offset);
|
|
}
|
|
setOffsetElementList(nodes, baseNodeOrToken, lastNodeOrToken, offset, align) {
|
|
let setIndent = (token, baseToken) => this.setOffsetToken(token, offset, baseToken);
|
|
if (align) {
|
|
for (const n of nodes) {
|
|
if (n) {
|
|
if (!isBeginningOfLine(this.sourceCode, n)) {
|
|
const startLoc = n.loc.start;
|
|
const alignIndent = startLoc.column - /^\s*/u.exec(this.sourceCode.lines[startLoc.line - 1])[0].length;
|
|
setIndent = (token, baseToken) => this.setAlignIndent(token.range[0], alignIndent, baseToken.range[0]);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
this._setOffsetElementList(nodes, baseNodeOrToken, lastNodeOrToken, setIndent);
|
|
}
|
|
_setOffsetElementList(nodes, baseNodeOrToken, lastNodeOrToken, setIndent) {
|
|
const baseToken = this.sourceCode.getFirstToken(baseNodeOrToken);
|
|
let prevToken = this.sourceCode.getLastToken(baseNodeOrToken);
|
|
for (const node of nodes) {
|
|
if (node == null) {
|
|
continue;
|
|
}
|
|
const elementTokens = getFirstAndLastTokens(this.sourceCode, node, prevToken.range[1]);
|
|
let t = prevToken;
|
|
while ((t = this.sourceCode.getTokenAfter(t, {
|
|
includeComments: true,
|
|
filter: isNotWhitespace
|
|
})) != null &&
|
|
t.range[1] <= elementTokens.firstToken.range[0]) {
|
|
setIndent(t, baseToken);
|
|
}
|
|
setIndent(elementTokens.firstToken, baseToken);
|
|
prevToken = elementTokens.lastToken;
|
|
}
|
|
if (lastNodeOrToken) {
|
|
const lastToken = this.sourceCode.getFirstToken(lastNodeOrToken);
|
|
let t = prevToken;
|
|
while ((t = this.sourceCode.getTokenAfter(t, {
|
|
includeComments: true,
|
|
filter: isNotWhitespace
|
|
})) != null &&
|
|
t.range[1] <= lastToken.range[0]) {
|
|
setIndent(t, baseToken);
|
|
}
|
|
this.setOffsetToken(lastToken, 0, baseToken);
|
|
}
|
|
}
|
|
/**
|
|
* Ignore range of the given node.
|
|
*/
|
|
ignore(node) {
|
|
const range = node.range;
|
|
const n = this.ignoreRanges.get(range[0]) ?? 0;
|
|
this.ignoreRanges.set(range[0], Math.max(n, range[1]));
|
|
}
|
|
getOffsetCalculator() {
|
|
// eslint-disable-next-line @typescript-eslint/no-use-before-define -- ignore
|
|
return new OffsetCalculator({
|
|
offsets: this.offsets,
|
|
options: this.options,
|
|
ignoreRanges: [...this.ignoreRanges]
|
|
});
|
|
}
|
|
}
|
|
export class OffsetCalculator {
|
|
constructor(arg) {
|
|
this.offsets = arg.offsets;
|
|
this.options = arg.options;
|
|
this.ignoreRanges = arg.ignoreRanges;
|
|
}
|
|
/**
|
|
* Calculate correct indentation of the given index.
|
|
*/
|
|
getExpectedIndentFromIndex(index) {
|
|
const offsetInfo = this.offsets.get(index);
|
|
if (offsetInfo == null) {
|
|
return null;
|
|
}
|
|
if (offsetInfo.expectedIndent != null) {
|
|
return offsetInfo.expectedIndent;
|
|
}
|
|
if (offsetInfo.type === 2 /* OffsetDataType.start */) {
|
|
return offsetInfo.offset * this.options.indentSize;
|
|
}
|
|
const baseIndent = this.getExpectedIndentFromIndex(offsetInfo.base);
|
|
if (baseIndent == null) {
|
|
return null;
|
|
}
|
|
if (offsetInfo.type === 1 /* OffsetDataType.align */) {
|
|
return baseIndent + offsetInfo.alignIndent;
|
|
}
|
|
return baseIndent + offsetInfo.offset * this.options.indentSize;
|
|
}
|
|
/**
|
|
* Calculate correct indentation of the given token.
|
|
*/
|
|
getExpectedIndentFromToken(token) {
|
|
return this.getExpectedIndentFromIndex(token.range[0]);
|
|
}
|
|
/**
|
|
* Calculate correct indentation of the line of the given tokens.
|
|
*/
|
|
getExpectedIndentFromTokens(tokens) {
|
|
for (const token of tokens) {
|
|
const index = token.range[0];
|
|
if (this.ignoreRanges.some(([f, t]) => f <= index && index < t)) {
|
|
return null;
|
|
}
|
|
const expectedIndent = this.getExpectedIndentFromIndex(index);
|
|
if (expectedIndent != null) {
|
|
return expectedIndent;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
/** Save expected indent to give tokens */
|
|
saveExpectedIndent(tokens, expectedIndent) {
|
|
for (const token of tokens) {
|
|
const offsetInfo = this.offsets.get(token.range[0]);
|
|
if (offsetInfo == null) {
|
|
continue;
|
|
}
|
|
offsetInfo.expectedIndent = offsetInfo.expectedIndent ?? expectedIndent;
|
|
}
|
|
}
|
|
}
|