Skip to content
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/two-frogs-hammer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"svelte": patch
---

feat: add support for svelte inspector
2 changes: 1 addition & 1 deletion packages/svelte/src/compiler/phases/2-analyze/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { DelegatedEvents, namespace_mathml, namespace_svg } from '../../../const
import { should_proxy_or_freeze } from '../3-transform/client/utils.js';
import { analyze_css } from './css/css-analyze.js';
import { prune } from './css/css-prune.js';
import { hash } from './utils.js';
import { hash } from '../../utils.js';
import { warn_unused } from './css/css-warn.js';
import { extract_svelte_ignore } from '../../utils/extract_svelte_ignore.js';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { javascript_visitors_runes } from './visitors/javascript-runes.js';
import { javascript_visitors_legacy } from './visitors/javascript-legacy.js';
import { serialize_get_binding } from './utils.js';
import { render_stylesheet } from '../css/index.js';
import { getLocator } from 'locate-character';

/**
* This function ensures visitor sets don't accidentally clobber each other
Expand Down Expand Up @@ -47,6 +48,7 @@ export function client_component(source, analysis, options) {
scopes: analysis.template.scopes,
hoisted: [b.import_all('$', 'svelte/internal/client')],
node: /** @type {any} */ (null), // populated by the root node
source_locator: getLocator(source, { offsetLine: 1 }),
// these should be set by create_block - if they're called outside, it's a bug
get before_init() {
/** @type {any[]} */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type {
import type { Namespace, SvelteNode, ValidatedCompileOptions } from '#compiler';
import type { TransformState } from '../types.js';
import type { ComponentAnalysis } from '../../types.js';
import type { Location } from 'locate-character';

export interface ClientTransformState extends TransformState {
readonly private_state: Map<string, StateField>;
Expand All @@ -28,6 +29,10 @@ export interface ComponentClientTransformState extends ClientTransformState {
readonly options: ValidatedCompileOptions;
readonly hoisted: Array<Statement | ModuleDeclaration>;
readonly events: Set<string>;
readonly source_locator: (
search: string | number,
index?: number | undefined
) => Location | undefined;

/** Stuff that happens before the render effect(s) */
readonly before_init: Statement[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import { regex_is_valid_identifier } from '../../../patterns.js';
import { javascript_visitors_runes } from './javascript-runes.js';
import { sanitize_template_string } from '../../../../utils/sanitize_template_string.js';
import { walk } from 'zimmerframe';
import { hash } from '../../../../utils.js';

/**
* @param {import('#compiler').RegularElement | import('#compiler').SvelteElement} element
Expand Down Expand Up @@ -959,6 +960,16 @@ function serialize_bind_this(bind_this, context, node) {
return b.call('$.bind_this', ...args);
}

/**
* @param {import("estree").Expression[]} args
* @param {import("../types.js").ComponentClientTransformState} state
*/
function push_dev_inspector_data(args, state) {
if (state.options.dev && state.options.filename) {
args.push(b.literal(state.options.filename), b.literal('sloc' + hash(state.options.filename)));
}
}

/**
* Creates a new block which looks roughly like this:
* ```js
Expand Down Expand Up @@ -1050,7 +1061,13 @@ function create_block(parent, name, nodes, context) {
);

body.push(b.var(id, b.call(template_name)), ...state.before_init, ...state.init);
close = b.stmt(b.call('$.append', b.id('$$anchor'), id));

/** @type {import('estree').Expression[]} */
const anchor_args = [id];

push_dev_inspector_data(anchor_args, state);

close = b.stmt(b.call('$.append', b.id('$$anchor'), ...anchor_args));
} else if (is_single_child_not_needing_template) {
context.visit(trimmed[0], state);
body.push(...state.before_init, ...state.init);
Expand All @@ -1071,7 +1088,13 @@ function create_block(parent, name, nodes, context) {
});

body.push(b.var(id, b.call('$.text', b.id('$$anchor'))), ...state.before_init, ...state.init);
close = b.stmt(b.call('$.append', b.id('$$anchor'), id));

/** @type {import('estree').Expression[]} */
const args = [id];

push_dev_inspector_data(args, state);

close = b.stmt(b.call('$.append', b.id('$$anchor'), ...args));
} else {
/** @type {(is_text: boolean) => import('estree').Expression} */
const expression = (is_text) =>
Expand Down Expand Up @@ -1107,7 +1130,12 @@ function create_block(parent, name, nodes, context) {

body.push(...state.before_init, ...state.init);

close = b.stmt(b.call('$.append', b.id('$$anchor'), id));
/** @type {import('estree').Expression[]} */
const args = [id];

push_dev_inspector_data(args, state);

close = b.stmt(b.call('$.append', b.id('$$anchor'), ...args));
}
} else {
body.push(...state.before_init, ...state.init);
Expand Down Expand Up @@ -1991,6 +2019,16 @@ export const template_visitors = {
serialize_class_directives(class_directives, node_id, context, is_attributes_reactive);
serialize_style_directives(style_directives, node_id, context, is_attributes_reactive);

if (context.state.options.dev && context.state.options.filename) {
const start = context.state.source_locator(node.start);

if (start) {
context.state.template.push(
` sloc${hash(context.state.options.filename)}="${start.line}:${start.column}"`
);
}
}

context.state.template.push('>');

/** @type {import('../types').ComponentClientTransformState} */
Expand Down
2 changes: 1 addition & 1 deletion packages/svelte/src/compiler/phases/3-transform/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export function transform_component(analysis, source, options) {

const program =
options.generate === 'server'
? server_component(analysis, options)
? server_component(source, analysis, options)
: client_component(source, analysis, options);

const js_source_name = get_source_name(options.filename, options.outputFilename, 'input.svelte');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import { DOMBooleanAttributes, HYDRATION_END, HYDRATION_START } from '../../../.
import { escape_html } from '../../../../escaping.js';
import { sanitize_template_string } from '../../../utils/sanitize_template_string.js';
import { BLOCK_CLOSE, BLOCK_CLOSE_ELSE } from '../../../../internal/server/hydration.js';
import { getLocator } from 'locate-character';
import { hash } from '../../../utils.js';

export const block_open = t_string(`<!--${HYDRATION_START}-->`);
export const block_close = t_string(`<!--${HYDRATION_END}-->`);
Expand Down Expand Up @@ -1326,6 +1328,17 @@ const template_visitors = {

context.state.template.push(t_string(`<${node.name}`));
const body_expression = serialize_element_attributes(node, context);

if (context.state.options.dev && context.state.options.filename) {
const start = context.state.source_locator(node.start);

if (start) {
context.state.template.push(
t_string(` sloc${hash(context.state.options.filename)}="${start.line}:${start.column}"`)
);
}
}

context.state.template.push(t_string('>'));

/** @type {import('./types').ComponentServerTransformState} */
Expand Down Expand Up @@ -2091,18 +2104,20 @@ function serialize_style_directives(style_directives, style_attribute, context)
}

/**
* @param {string} source
* @param {import('../../types').ComponentAnalysis} analysis
* @param {import('#compiler').ValidatedCompileOptions} options
* @returns {import('estree').Program}
*/
export function server_component(analysis, options) {
export function server_component(source, analysis, options) {
/** @type {import('./types').ComponentServerTransformState} */
const state = {
analysis,
options,
scope: analysis.module.scope,
scopes: analysis.template.scopes,
hoisted: [b.import_all('$', 'svelte/internal/server')],
source_locator: getLocator(source, { offsetLine: 1 }),
legacy_reactive_statements: new Map(),
// these should be set by create_block - if they're called outside, it's a bug
get init() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type { SvelteNode, Namespace, ValidatedCompileOptions } from '#compiler';
import type { TransformState } from '../types.js';
import type { ComponentAnalysis } from '../../types.js';
import type { StateField } from '../client/types.js';
import type { Location } from 'locate-character';

export type TemplateExpression = {
type: 'expression';
Expand Down Expand Up @@ -47,6 +48,11 @@ export interface ComponentServerTransformState extends ServerTransformState {

readonly hoisted: Array<Statement | ModuleDeclaration>;

readonly source_locator: (
search: string | number,
index?: number | undefined
) => Location | undefined;

/** The SSR template */
readonly template: Template[];
readonly metadata: {
Expand Down
34 changes: 33 additions & 1 deletion packages/svelte/src/internal/client/dom/template.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { current_effect } from '../runtime.js';
import { TEMPLATE_FRAGMENT, TEMPLATE_USE_IMPORT_NODE } from '../../../constants.js';
import { effect } from '../reactivity/effects.js';
import { is_array } from '../utils.js';
import { DEV } from 'esm-env';

/**
* @template {import("#client").TemplateNode | import("#client").TemplateNode[]} T
Expand Down Expand Up @@ -33,6 +34,30 @@ export function push_template_node(
return dom;
}

/**
* @param {string} filename
* @param {string} filename_hash
*/
function attach_inspector_metadata(filename, filename_hash) {
const nodes = document.querySelectorAll(`*[${filename_hash}]`);

for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
const loc = node.getAttribute(filename_hash)?.split(':');
node.removeAttribute(filename_hash);
if (loc) {
// @ts-expect-error
node.__svelte_meta = {
loc: {
file: filename,
line: loc[0],
column: loc[1]
}
};
}
}
}

/**
* @param {string} content
* @param {number} flags
Expand Down Expand Up @@ -258,9 +283,16 @@ export const comment = template('<!>', TEMPLATE_FRAGMENT);
* and insert the elements into the dom (in client mode).
* @param {Text | Comment | Element} anchor
* @param {import('#client').Dom} dom
* @param {string} [filename]
* @param {string} [filename_hash]
*/
export function append(anchor, dom) {
export function append(anchor, dom, filename, filename_hash) {
if (!hydrating) {
anchor.before(/** @type {Node} */ (dom));
}
if (DEV && filename && filename_hash) {
effect(() => {
attach_inspector_metadata(filename, filename_hash);
});
}
}
6 changes: 4 additions & 2 deletions packages/svelte/tests/runtime-legacy/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,14 +254,16 @@ async function run_test_variant(
}

if (variant === 'ssr') {
const html_without_loc_attributes = target.innerHTML.replace(/\s?sloc-?\S*\=\"\d*:\d*"/g, '');

if (config.ssrHtml) {
assert_html_equal_with_options(target.innerHTML, config.ssrHtml, {
assert_html_equal_with_options(html_without_loc_attributes, config.ssrHtml, {
preserveComments:
config.withoutNormalizeHtml === 'only-strip-comments' ? false : undefined,
withoutNormalizeHtml: !!config.withoutNormalizeHtml
});
} else if (config.html) {
assert_html_equal_with_options(target.innerHTML, config.html, {
assert_html_equal_with_options(html_without_loc_attributes, config.html, {
preserveComments:
config.withoutNormalizeHtml === 'only-strip-comments' ? false : undefined,
withoutNormalizeHtml: !!config.withoutNormalizeHtml
Expand Down
6 changes: 5 additions & 1 deletion playgrounds/demo/vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ import { defineConfig } from 'vite';
import { svelte } from '@sveltejs/vite-plugin-svelte';

export default defineConfig({
plugins: [svelte()],
plugins: [
svelte({
inspector: true
})
],
optimizeDeps: {
// svelte is a local workspace package, optimizing it would require dev server restarts with --force for every change
exclude: ['svelte']
Expand Down