feat(frontend): scaffold SvelteKit with TS 7.0 (native-preview) and tsgo
This commit is contained in:
+504
@@ -0,0 +1,504 @@
|
||||
const regex_return_characters = /\r/g;
|
||||
|
||||
/**
|
||||
* @param {string} str
|
||||
* @returns {string}
|
||||
*/
|
||||
export function hash(str) {
|
||||
str = str.replace(regex_return_characters, '');
|
||||
let hash = 5381;
|
||||
let i = str.length;
|
||||
|
||||
while (i--) hash = ((hash << 5) - hash) ^ str.charCodeAt(i);
|
||||
return (hash >>> 0).toString(36);
|
||||
}
|
||||
|
||||
const VOID_ELEMENT_NAMES = [
|
||||
'area',
|
||||
'base',
|
||||
'br',
|
||||
'col',
|
||||
'command',
|
||||
'embed',
|
||||
'hr',
|
||||
'img',
|
||||
'input',
|
||||
'keygen',
|
||||
'link',
|
||||
'meta',
|
||||
'param',
|
||||
'source',
|
||||
'track',
|
||||
'wbr'
|
||||
];
|
||||
|
||||
/**
|
||||
* Returns `true` if `name` is of a void element
|
||||
* @param {string} name
|
||||
*/
|
||||
export function is_void(name) {
|
||||
return VOID_ELEMENT_NAMES.includes(name) || name.toLowerCase() === '!doctype';
|
||||
}
|
||||
|
||||
const RESERVED_WORDS = [
|
||||
'arguments',
|
||||
'await',
|
||||
'break',
|
||||
'case',
|
||||
'catch',
|
||||
'class',
|
||||
'const',
|
||||
'continue',
|
||||
'debugger',
|
||||
'default',
|
||||
'delete',
|
||||
'do',
|
||||
'else',
|
||||
'enum',
|
||||
'eval',
|
||||
'export',
|
||||
'extends',
|
||||
'false',
|
||||
'finally',
|
||||
'for',
|
||||
'function',
|
||||
'if',
|
||||
'implements',
|
||||
'import',
|
||||
'in',
|
||||
'instanceof',
|
||||
'interface',
|
||||
'let',
|
||||
'new',
|
||||
'null',
|
||||
'package',
|
||||
'private',
|
||||
'protected',
|
||||
'public',
|
||||
'return',
|
||||
'static',
|
||||
'super',
|
||||
'switch',
|
||||
'this',
|
||||
'throw',
|
||||
'true',
|
||||
'try',
|
||||
'typeof',
|
||||
'var',
|
||||
'void',
|
||||
'while',
|
||||
'with',
|
||||
'yield'
|
||||
];
|
||||
|
||||
/**
|
||||
* Returns `true` if `word` is a reserved JavaScript keyword
|
||||
* @param {string} word
|
||||
*/
|
||||
export function is_reserved(word) {
|
||||
return RESERVED_WORDS.includes(word);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
*/
|
||||
export function is_capture_event(name) {
|
||||
return name.endsWith('capture') && name !== 'gotpointercapture' && name !== 'lostpointercapture';
|
||||
}
|
||||
|
||||
/** List of Element events that will be delegated */
|
||||
const DELEGATED_EVENTS = [
|
||||
'beforeinput',
|
||||
'click',
|
||||
'change',
|
||||
'dblclick',
|
||||
'contextmenu',
|
||||
'focusin',
|
||||
'focusout',
|
||||
'input',
|
||||
'keydown',
|
||||
'keyup',
|
||||
'mousedown',
|
||||
'mousemove',
|
||||
'mouseout',
|
||||
'mouseover',
|
||||
'mouseup',
|
||||
'pointerdown',
|
||||
'pointermove',
|
||||
'pointerout',
|
||||
'pointerover',
|
||||
'pointerup',
|
||||
'touchend',
|
||||
'touchmove',
|
||||
'touchstart'
|
||||
];
|
||||
|
||||
/**
|
||||
* Returns `true` if `event_name` is a delegated event
|
||||
* @param {string} event_name
|
||||
*/
|
||||
export function can_delegate_event(event_name) {
|
||||
return DELEGATED_EVENTS.includes(event_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attributes that are boolean, i.e. they are present or not present.
|
||||
*/
|
||||
const DOM_BOOLEAN_ATTRIBUTES = [
|
||||
'allowfullscreen',
|
||||
'async',
|
||||
'autofocus',
|
||||
'autoplay',
|
||||
'checked',
|
||||
'controls',
|
||||
'default',
|
||||
'disabled',
|
||||
'formnovalidate',
|
||||
'indeterminate',
|
||||
'inert',
|
||||
'ismap',
|
||||
'loop',
|
||||
'multiple',
|
||||
'muted',
|
||||
'nomodule',
|
||||
'novalidate',
|
||||
'open',
|
||||
'playsinline',
|
||||
'readonly',
|
||||
'required',
|
||||
'reversed',
|
||||
'seamless',
|
||||
'selected',
|
||||
'webkitdirectory',
|
||||
'defer',
|
||||
'disablepictureinpicture',
|
||||
'disableremoteplayback'
|
||||
];
|
||||
|
||||
/**
|
||||
* Returns `true` if `name` is a boolean attribute
|
||||
* @param {string} name
|
||||
*/
|
||||
export function is_boolean_attribute(name) {
|
||||
return DOM_BOOLEAN_ATTRIBUTES.includes(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {Record<string, string>}
|
||||
* List of attribute names that should be aliased to their property names
|
||||
* because they behave differently between setting them as an attribute and
|
||||
* setting them as a property.
|
||||
*/
|
||||
const ATTRIBUTE_ALIASES = {
|
||||
// no `class: 'className'` because we handle that separately
|
||||
formnovalidate: 'formNoValidate',
|
||||
ismap: 'isMap',
|
||||
nomodule: 'noModule',
|
||||
playsinline: 'playsInline',
|
||||
readonly: 'readOnly',
|
||||
defaultvalue: 'defaultValue',
|
||||
defaultchecked: 'defaultChecked',
|
||||
srcobject: 'srcObject',
|
||||
novalidate: 'noValidate',
|
||||
allowfullscreen: 'allowFullscreen',
|
||||
disablepictureinpicture: 'disablePictureInPicture',
|
||||
disableremoteplayback: 'disableRemotePlayback'
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
*/
|
||||
export function normalize_attribute(name) {
|
||||
name = name.toLowerCase();
|
||||
return ATTRIBUTE_ALIASES[name] ?? name;
|
||||
}
|
||||
|
||||
const DOM_PROPERTIES = [
|
||||
...DOM_BOOLEAN_ATTRIBUTES,
|
||||
'formNoValidate',
|
||||
'isMap',
|
||||
'noModule',
|
||||
'playsInline',
|
||||
'readOnly',
|
||||
'value',
|
||||
'volume',
|
||||
'defaultValue',
|
||||
'defaultChecked',
|
||||
'srcObject',
|
||||
'noValidate',
|
||||
'allowFullscreen',
|
||||
'disablePictureInPicture',
|
||||
'disableRemotePlayback'
|
||||
];
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
*/
|
||||
export function is_dom_property(name) {
|
||||
return DOM_PROPERTIES.includes(name);
|
||||
}
|
||||
|
||||
const NON_STATIC_PROPERTIES = ['autofocus', 'muted', 'defaultValue', 'defaultChecked'];
|
||||
|
||||
/**
|
||||
* Returns `true` if the given attribute cannot be set through the template
|
||||
* string, i.e. needs some kind of JavaScript handling to work.
|
||||
* @param {string} name
|
||||
*/
|
||||
export function cannot_be_set_statically(name) {
|
||||
return NON_STATIC_PROPERTIES.includes(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subset of delegated events which should be passive by default.
|
||||
* These two are already passive via browser defaults on window, document and body.
|
||||
* But since
|
||||
* - we're delegating them
|
||||
* - they happen often
|
||||
* - they apply to mobile which is generally less performant
|
||||
* we're marking them as passive by default for other elements, too.
|
||||
*/
|
||||
const PASSIVE_EVENTS = ['touchstart', 'touchmove'];
|
||||
|
||||
/**
|
||||
* Returns `true` if `name` is a passive event
|
||||
* @param {string} name
|
||||
*/
|
||||
export function is_passive_event(name) {
|
||||
return PASSIVE_EVENTS.includes(name);
|
||||
}
|
||||
|
||||
const CONTENT_EDITABLE_BINDINGS = ['textContent', 'innerHTML', 'innerText'];
|
||||
|
||||
/** @param {string} name */
|
||||
export function is_content_editable_binding(name) {
|
||||
return CONTENT_EDITABLE_BINDINGS.includes(name);
|
||||
}
|
||||
|
||||
const LOAD_ERROR_ELEMENTS = [
|
||||
'body',
|
||||
'embed',
|
||||
'iframe',
|
||||
'img',
|
||||
'link',
|
||||
'object',
|
||||
'script',
|
||||
'style',
|
||||
'track'
|
||||
];
|
||||
|
||||
/**
|
||||
* Returns `true` if the element emits `load` and `error` events
|
||||
* @param {string} name
|
||||
*/
|
||||
export function is_load_error_element(name) {
|
||||
return LOAD_ERROR_ELEMENTS.includes(name);
|
||||
}
|
||||
|
||||
const SVG_ELEMENTS = [
|
||||
'altGlyph',
|
||||
'altGlyphDef',
|
||||
'altGlyphItem',
|
||||
'animate',
|
||||
'animateColor',
|
||||
'animateMotion',
|
||||
'animateTransform',
|
||||
'circle',
|
||||
'clipPath',
|
||||
'color-profile',
|
||||
'cursor',
|
||||
'defs',
|
||||
'desc',
|
||||
'discard',
|
||||
'ellipse',
|
||||
'feBlend',
|
||||
'feColorMatrix',
|
||||
'feComponentTransfer',
|
||||
'feComposite',
|
||||
'feConvolveMatrix',
|
||||
'feDiffuseLighting',
|
||||
'feDisplacementMap',
|
||||
'feDistantLight',
|
||||
'feDropShadow',
|
||||
'feFlood',
|
||||
'feFuncA',
|
||||
'feFuncB',
|
||||
'feFuncG',
|
||||
'feFuncR',
|
||||
'feGaussianBlur',
|
||||
'feImage',
|
||||
'feMerge',
|
||||
'feMergeNode',
|
||||
'feMorphology',
|
||||
'feOffset',
|
||||
'fePointLight',
|
||||
'feSpecularLighting',
|
||||
'feSpotLight',
|
||||
'feTile',
|
||||
'feTurbulence',
|
||||
'filter',
|
||||
'font',
|
||||
'font-face',
|
||||
'font-face-format',
|
||||
'font-face-name',
|
||||
'font-face-src',
|
||||
'font-face-uri',
|
||||
'foreignObject',
|
||||
'g',
|
||||
'glyph',
|
||||
'glyphRef',
|
||||
'hatch',
|
||||
'hatchpath',
|
||||
'hkern',
|
||||
'image',
|
||||
'line',
|
||||
'linearGradient',
|
||||
'marker',
|
||||
'mask',
|
||||
'mesh',
|
||||
'meshgradient',
|
||||
'meshpatch',
|
||||
'meshrow',
|
||||
'metadata',
|
||||
'missing-glyph',
|
||||
'mpath',
|
||||
'path',
|
||||
'pattern',
|
||||
'polygon',
|
||||
'polyline',
|
||||
'radialGradient',
|
||||
'rect',
|
||||
'set',
|
||||
'solidcolor',
|
||||
'stop',
|
||||
'svg',
|
||||
'switch',
|
||||
'symbol',
|
||||
'text',
|
||||
'textPath',
|
||||
'tref',
|
||||
'tspan',
|
||||
'unknown',
|
||||
'use',
|
||||
'view',
|
||||
'vkern'
|
||||
];
|
||||
|
||||
/** @param {string} name */
|
||||
export function is_svg(name) {
|
||||
return SVG_ELEMENTS.includes(name);
|
||||
}
|
||||
|
||||
const MATHML_ELEMENTS = [
|
||||
'annotation',
|
||||
'annotation-xml',
|
||||
'maction',
|
||||
'math',
|
||||
'merror',
|
||||
'mfrac',
|
||||
'mi',
|
||||
'mmultiscripts',
|
||||
'mn',
|
||||
'mo',
|
||||
'mover',
|
||||
'mpadded',
|
||||
'mphantom',
|
||||
'mprescripts',
|
||||
'mroot',
|
||||
'mrow',
|
||||
'ms',
|
||||
'mspace',
|
||||
'msqrt',
|
||||
'mstyle',
|
||||
'msub',
|
||||
'msubsup',
|
||||
'msup',
|
||||
'mtable',
|
||||
'mtd',
|
||||
'mtext',
|
||||
'mtr',
|
||||
'munder',
|
||||
'munderover',
|
||||
'semantics'
|
||||
];
|
||||
|
||||
/** @param {string} name */
|
||||
export function is_mathml(name) {
|
||||
return MATHML_ELEMENTS.includes(name);
|
||||
}
|
||||
|
||||
const STATE_CREATION_RUNES = /** @type {const} */ ([
|
||||
'$state',
|
||||
'$state.raw',
|
||||
'$derived',
|
||||
'$derived.by'
|
||||
]);
|
||||
|
||||
const RUNES = /** @type {const} */ ([
|
||||
...STATE_CREATION_RUNES,
|
||||
'$state.eager',
|
||||
'$state.snapshot',
|
||||
'$props',
|
||||
'$props.id',
|
||||
'$bindable',
|
||||
'$effect',
|
||||
'$effect.pre',
|
||||
'$effect.tracking',
|
||||
'$effect.root',
|
||||
'$effect.pending',
|
||||
'$inspect',
|
||||
'$inspect().with',
|
||||
'$inspect.trace',
|
||||
'$host'
|
||||
]);
|
||||
|
||||
/** @typedef {typeof RUNES[number]} RuneName */
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @returns {name is RuneName}
|
||||
*/
|
||||
export function is_rune(name) {
|
||||
return RUNES.includes(/** @type {RuneName} */ (name));
|
||||
}
|
||||
|
||||
/** @typedef {typeof STATE_CREATION_RUNES[number]} StateCreationRuneName */
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @returns {name is StateCreationRuneName}
|
||||
*/
|
||||
export function is_state_creation_rune(name) {
|
||||
return STATE_CREATION_RUNES.includes(/** @type {StateCreationRuneName} */ (name));
|
||||
}
|
||||
|
||||
/** List of elements that require raw contents and should not have SSR comments put in them */
|
||||
const RAW_TEXT_ELEMENTS = /** @type {const} */ (['textarea', 'script', 'style', 'title']);
|
||||
|
||||
/** @param {string} name */
|
||||
export function is_raw_text_element(name) {
|
||||
return RAW_TEXT_ELEMENTS.includes(/** @type {typeof RAW_TEXT_ELEMENTS[number]} */ (name));
|
||||
}
|
||||
|
||||
// Matches valid HTML/SVG/MathML element names and custom element names.
|
||||
// https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name
|
||||
//
|
||||
// Standard elements: ASCII alpha start, followed by ASCII alphanumerics.
|
||||
// Custom elements: ASCII alpha start, followed by any mix of PCENChar (which
|
||||
// includes ASCII alphanumerics, `-`, `.`, `_`, and specified Unicode ranges),
|
||||
// with at least one hyphen required somewhere after the first character.
|
||||
//
|
||||
// Rejects strings containing whitespace, quotes, angle brackets, slashes, equals,
|
||||
// or other characters that could break out of a tag-name token and enable markup injection.
|
||||
export const REGEX_VALID_TAG_NAME =
|
||||
/^[a-zA-Z][a-zA-Z0-9]*(-[a-zA-Z0-9.\-_\u00B7\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u037D\u037F-\u1FFF\u200C-\u200D\u203F-\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\u{10000}-\u{EFFFF}]+)*$/u;
|
||||
|
||||
/**
|
||||
* Prevent devtools trying to make `location` a clickable link by inserting a zero-width space
|
||||
* @template {string | undefined} T
|
||||
* @param {T} location
|
||||
* @returns {T};
|
||||
*/
|
||||
export function sanitize_location(location) {
|
||||
return /** @type {T} */ (location?.replace(/\//g, '/\u200b'));
|
||||
}
|
||||
Reference in New Issue
Block a user