Skip to content

Stats #1299

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Apr 8, 2018
Merged

Stats #1299

Show file tree
Hide file tree
Changes from all 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
102 changes: 102 additions & 0 deletions src/Stats.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { Node, Warning } from './interfaces';
import Generator from './generators/Generator';

const now = (typeof process !== 'undefined' && process.hrtime)
? () => {
const t = process.hrtime();
return t[0] * 1e3 + t[1] / 1e6;
}
: () => window.performance.now();

type Timing = {
label: string;
start: number;
end: number;
children: Timing[];
}

function collapseTimings(timings) {
const result = {};
timings.forEach(timing => {
result[timing.label] = Object.assign({
total: timing.end - timing.start
}, timing.children && collapseTimings(timing.children));
});
return result;
}

export default class Stats {
startTime: number;
currentTiming: Timing;
currentChildren: Timing[];
timings: Timing[];
stack: Timing[];
warnings: Warning[];

constructor() {
this.startTime = now();
this.stack = [];
this.currentChildren = this.timings = [];

this.warnings = [];
}

start(label) {
const timing = {
label,
start: now(),
end: null,
children: []
};

this.currentChildren.push(timing);
this.stack.push(timing);

this.currentTiming = timing;
this.currentChildren = timing.children;
}

stop(label) {
if (label !== this.currentTiming.label) {
throw new Error(`Mismatched timing labels`);
}

this.currentTiming.end = now();
this.stack.pop();
this.currentTiming = this.stack[this.stack.length - 1];
this.currentChildren = this.currentTiming ? this.currentTiming.children : this.timings;
}

render(generator: Generator) {
const timings = Object.assign({
total: now() - this.startTime
}, collapseTimings(this.timings));

const imports = generator.imports.map(node => {
return {
source: node.source.value,
specifiers: node.specifiers.map(specifier => {
return {
name: (
specifier.type === 'ImportDefaultSpecifier' ? 'default' :
specifier.type === 'ImportNamespaceSpecifier' ? '*' :
specifier.imported.name
),
as: specifier.local.name
};
})
}
});

const hooks: Record<string, boolean> = {};
if (generator.templateProperties.oncreate) hooks.oncreate = true;
if (generator.templateProperties.ondestroy) hooks.ondestroy = true;

return {
timings,
warnings: this.warnings,
imports,
hooks
};
}
}
10 changes: 10 additions & 0 deletions src/generators/Generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import MagicString, { Bundle } from 'magic-string';
import isReference from 'is-reference';
import { walk, childKeys } from 'estree-walker';
import { getLocator } from 'locate-character';
import Stats from '../Stats';
import deindent from '../utils/deindent';
import CodeBuilder from '../utils/CodeBuilder';
import getCodeFrame from '../utils/getCodeFrame';
Expand Down Expand Up @@ -76,6 +77,8 @@ childKeys.EachBlock = childKeys.IfBlock = ['children', 'else'];
childKeys.Attribute = ['value'];

export default class Generator {
stats: Stats;

ast: Parsed;
parsed: Parsed;
source: string;
Expand Down Expand Up @@ -123,8 +126,12 @@ export default class Generator {
name: string,
stylesheet: Stylesheet,
options: CompileOptions,
stats: Stats,
dom: boolean
) {
stats.start('compile');
this.stats = stats;

this.ast = clone(parsed);

this.parsed = parsed;
Expand Down Expand Up @@ -372,10 +379,13 @@ export default class Generator {
}
});

this.stats.stop('compile');

return {
ast: this.ast,
js,
css,
stats: this.stats.render(this),

// TODO deprecate
code: js.code,
Expand Down
11 changes: 7 additions & 4 deletions src/generators/dom/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import reservedNames from '../../utils/reservedNames';
import shared from './shared';
import Generator from '../Generator';
import Stylesheet from '../../css/Stylesheet';
import Stats from '../../Stats';
import Block from './Block';
import { test } from '../../config';
import { Parsed, CompileOptions, Node } from '../../interfaces';
Expand All @@ -34,9 +35,10 @@ export class DomGenerator extends Generator {
source: string,
name: string,
stylesheet: Stylesheet,
options: CompileOptions
options: CompileOptions,
stats: Stats
) {
super(parsed, source, name, stylesheet, options, true);
super(parsed, source, name, stylesheet, options, stats, true);
this.blocks = [];

this.readonly = new Set();
Expand Down Expand Up @@ -81,11 +83,12 @@ export default function dom(
parsed: Parsed,
source: string,
stylesheet: Stylesheet,
options: CompileOptions
options: CompileOptions,
stats: Stats
) {
const format = options.format || 'es';

const generator = new DomGenerator(parsed, source, options.name || 'SvelteComponent', stylesheet, options);
const generator = new DomGenerator(parsed, source, options.name || 'SvelteComponent', stylesheet, options, stats);

const {
computations,
Expand Down
11 changes: 7 additions & 4 deletions src/generators/server-side-rendering/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import deindent from '../../utils/deindent';
import Generator from '../Generator';
import Stats from '../../Stats';
import Stylesheet from '../../css/Stylesheet';
import Block from './Block';
import visit from './visit';
Expand All @@ -20,9 +21,10 @@ export class SsrGenerator extends Generator {
source: string,
name: string,
stylesheet: Stylesheet,
options: CompileOptions
options: CompileOptions,
stats: Stats
) {
super(parsed, source, name, stylesheet, options, false);
super(parsed, source, name, stylesheet, options, stats, false);
this.bindings = [];
this.renderCode = '';
this.appendTargets = [];
Expand All @@ -45,11 +47,12 @@ export default function ssr(
parsed: Parsed,
source: string,
stylesheet: Stylesheet,
options: CompileOptions
options: CompileOptions,
stats: Stats
) {
const format = options.format || 'cjs';

const generator = new SsrGenerator(parsed, source, options.name || 'SvelteComponent', stylesheet, options);
const generator = new SsrGenerator(parsed, source, options.name || 'SvelteComponent', stylesheet, options, stats);

const { computations, name, templateProperties } = generator;

Expand Down
19 changes: 18 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import parse from './parse/index';
import validate from './validate/index';
import generate from './generators/dom/index';
import generateSSR from './generators/server-side-rendering/index';
import Stats from './Stats';
import { assign } from './shared/index.js';
import Stylesheet from './css/Stylesheet';
import { Parsed, CompileOptions, Warning, PreprocessOptions, Preprocessor } from './interfaces';
Expand Down Expand Up @@ -109,20 +110,36 @@ export function compile(source: string, _options: CompileOptions) {
const options = normalizeOptions(_options);
let parsed: Parsed;

const stats = new Stats();

try {
stats.start('parse');
parsed = parse(source, options);
stats.stop('parse');
} catch (err) {
options.onerror(err);
return;
}

stats.start('stylesheet');
const stylesheet = new Stylesheet(source, parsed, options.filename, options.cascade !== false, options.dev);
stats.stop('stylesheet');

stats.start('validate');
// TODO remove this when we remove svelte.validate from public API — we
// can use the stats object instead
const onwarn = options.onwarn;
options.onwarn = warning => {
stats.warnings.push(warning);
onwarn(warning);
};

validate(parsed, source, stylesheet, options);
stats.stop('validate');

const compiler = options.generate === 'ssr' ? generateSSR : generate;

return compiler(parsed, source, stylesheet, options);
return compiler(parsed, source, stylesheet, options, stats);
};

export function create(source: string, _options: CompileOptions = {}) {
Expand Down
2 changes: 1 addition & 1 deletion src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,4 @@ export interface PreprocessOptions {
filename?: string
}

export type Preprocessor = (options: {content: string, attributes: Record<string, string | boolean>, filename?: string}) => { code: string, map?: SourceMap | string };
export type Preprocessor = (options: {content: string, attributes: Record<string, string | boolean>, filename?: string}) => { code: string, map?: SourceMap | string };
1 change: 1 addition & 0 deletions src/validate/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import validateJs from './js/index';
import validateHtml from './html/index';
import { getLocator, Location } from 'locate-character';
import getCodeFrame from '../utils/getCodeFrame';
import Stats from '../Stats';
import error from '../utils/error';
import Stylesheet from '../css/Stylesheet';
import { Node, Parsed, CompileOptions, Warning } from '../interfaces';
Expand Down
59 changes: 59 additions & 0 deletions test/stats/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import * as fs from 'fs';
import assert from 'assert';
import { svelte, loadConfig, tryToLoadJson } from '../helpers.js';

describe('stats', () => {
fs.readdirSync('test/stats/samples').forEach(dir => {
if (dir[0] === '.') return;

// add .solo to a sample directory name to only run that test
const solo = /\.solo/.test(dir);
const skip = /\.skip/.test(dir);

if (solo && process.env.CI) {
throw new Error('Forgot to remove `solo: true` from test');
}

(solo ? it.only : skip ? it.skip : it)(dir, () => {
const config = loadConfig(`./stats/samples/${dir}/_config.js`);
const filename = `test/stats/samples/${dir}/input.html`;
const input = fs.readFileSync(filename, 'utf-8').replace(/\s+$/, '');

const expectedWarnings =
tryToLoadJson(`test/stats/samples/${dir}/warnings.json`) || [];
const expectedError = tryToLoadJson(
`test/stats/samples/${dir}/error.json`
);

let result;
let error;

try {
result = svelte.compile(input, config.options);
} catch (e) {
error = e;
}

config.test(assert, result.stats);

if (result.stats.warnings.length || expectedWarnings.length) {
// TODO check warnings are added to stats.warnings
}

if (error || expectedError) {
if (error && !expectedError) {
throw error;
}

if (expectedError && !error) {
throw new Error(`Expected an error: ${expectedError.message}`);
}

assert.equal(error.message, expectedError.message);
assert.deepEqual(error.loc, expectedError.loc);
assert.deepEqual(error.end, expectedError.end);
assert.equal(error.pos, expectedError.pos);
}
});
});
});
6 changes: 6 additions & 0 deletions test/stats/samples/basic/_config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default {
test(assert, stats) {
assert.equal(typeof stats.timings, 'object');
assert.equal(typeof stats.timings.total, 'number');
}
};
Empty file.
7 changes: 7 additions & 0 deletions test/stats/samples/hooks/_config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default {
test(assert, stats) {
assert.deepEqual(stats.hooks, {
oncreate: true
});
}
};
7 changes: 7 additions & 0 deletions test/stats/samples/hooks/input.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<script>
export default {
oncreate() {
console.log('creating');
}
};
</script>
18 changes: 18 additions & 0 deletions test/stats/samples/imports/_config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export default {
test(assert, stats) {
assert.deepEqual(stats.imports, [
{
source: 'x',
specifiers: [{ name: 'default', as: 'x' }]
},
{
source: 'y',
specifiers: [{ name: 'y', as: 'y' }]
},
{
source: 'z',
specifiers: [{ name: '*', as: 'z' }]
}
]);
}
};
5 changes: 5 additions & 0 deletions test/stats/samples/imports/input.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<script>
import x from 'x';
import { y } from 'y';
import * as z from 'z';
</script>
Loading