mirror of
https://github.com/danog/sass-site.git
synced 2025-01-06 04:59:18 +01:00
112 lines
3.3 KiB
TypeScript
112 lines
3.3 KiB
TypeScript
/* eslint-disable node/no-extraneous-import */
|
|
import {Diagnostic} from '@codemirror/lint';
|
|
import {Exception, Importer, OutputStyle, Syntax} from 'sass';
|
|
|
|
import {ConsoleLog, ConsoleLogDebug, ConsoleLogWarning} from './console-utils';
|
|
|
|
const PLAYGROUND_LOAD_ERROR_MESSAGE =
|
|
'The Sass Playground does not support loading stylesheets.';
|
|
|
|
export type PlaygroundState = {
|
|
inputFormat: Syntax;
|
|
outputFormat: OutputStyle;
|
|
inputValue: string;
|
|
compilerHasError: boolean;
|
|
debugOutput: ConsoleLog[];
|
|
};
|
|
|
|
// State is persisted to the URL's hash format in the following format:
|
|
// [inputFormat, outputFormat, ...inputValue] = hash;
|
|
// inputFormat: 0=indented 1=scss
|
|
// outputFormat: 0=compressed 1=expanded
|
|
export function stateToBase64(state: PlaygroundState): string {
|
|
const inputFormatChar = state.inputFormat === 'scss' ? 1 : 0;
|
|
const outputFormatChar = state.outputFormat === 'expanded' ? 1 : 0;
|
|
const persistedState = `${inputFormatChar}${outputFormatChar}${state.inputValue}`;
|
|
return btoa(encodeURIComponent(persistedState));
|
|
}
|
|
|
|
export function base64ToState(string: string): Partial<PlaygroundState> {
|
|
const state: Partial<PlaygroundState> = {};
|
|
let decoded;
|
|
try {
|
|
decoded = decodeURIComponent(atob(string));
|
|
} catch (error) {
|
|
return {};
|
|
}
|
|
|
|
if (!/\d\d.*/.test(decoded)) return {};
|
|
state.inputFormat = decoded.charAt(0) === '1' ? 'scss' : 'indented';
|
|
state.outputFormat = decoded.charAt(1) === '1' ? 'expanded' : 'compressed';
|
|
state.inputValue = decoded.slice(2);
|
|
|
|
return state;
|
|
}
|
|
|
|
type ParseResultSuccess = {css: string};
|
|
type ParseResultError = {error: Exception | unknown};
|
|
export type ParseResult = ParseResultSuccess | ParseResultError;
|
|
|
|
export function errorToDiagnostic(error: Exception | unknown): Diagnostic {
|
|
if (error instanceof Exception) {
|
|
return {
|
|
from: error.span.start.offset,
|
|
to: error.span.end.offset,
|
|
severity: 'error',
|
|
message: error.toString(),
|
|
};
|
|
} else {
|
|
let errorString = 'Unknown compilation error';
|
|
if (typeof error === 'string') errorString = error;
|
|
else if (typeof error?.toString() === 'string')
|
|
errorString = error.toString();
|
|
return {
|
|
from: 0,
|
|
to: 0,
|
|
severity: 'error',
|
|
message: errorString,
|
|
};
|
|
}
|
|
}
|
|
|
|
export function debugToDiagnostic(logItem: ConsoleLogDebug): Diagnostic {
|
|
return {
|
|
from: logItem.options.span.start.offset,
|
|
to: logItem.options.span.end.offset,
|
|
severity: 'info',
|
|
message: logItem.message,
|
|
};
|
|
}
|
|
|
|
export function warnToDiagnostic(
|
|
logItem: ConsoleLogWarning
|
|
): Diagnostic | null {
|
|
if (!logItem.options.span) return null;
|
|
return {
|
|
from: logItem.options.span.start.offset,
|
|
to: logItem.options.span.end.offset,
|
|
severity: 'warning',
|
|
message: logItem.message,
|
|
};
|
|
}
|
|
|
|
export function logsToDiagnostics(logs: ConsoleLog[]): Diagnostic[] {
|
|
const diagnostics = logs.flatMap(log => {
|
|
if (log.type === 'error') return errorToDiagnostic(log.error);
|
|
else if (log.type === 'warn') return warnToDiagnostic(log);
|
|
else if (log.type === 'debug') return debugToDiagnostic(log);
|
|
else return null;
|
|
});
|
|
// Remove empties
|
|
return diagnostics.filter(
|
|
(diagnostic): diagnostic is Diagnostic => !!diagnostic
|
|
);
|
|
}
|
|
|
|
export const customLoader: Importer<'sync'> = {
|
|
canonicalize() {
|
|
throw new Error(PLAYGROUND_LOAD_ERROR_MESSAGE, {cause: 'Test'});
|
|
},
|
|
load: () => null,
|
|
};
|