Skip to content

Commit

Permalink
feat(options-v2): Improve options validation for svelte v2 parser (Ka…
Browse files Browse the repository at this point in the history
  • Loading branch information
soft-decay committed Dec 6, 2020
1 parent 3bcb993 commit 5e2ef37
Showing 1 changed file with 122 additions and 52 deletions.
174 changes: 122 additions & 52 deletions lib/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,40 @@ const jsdoc = require('./jsdoc');

const hasOwnProperty = utils.hasOwnProperty;

const DEFAULT_OPTIONS = {
/**
* Flag, indicating that source locations should be extracted from source.
*/
includeSourceLocations: false,
range: false,
// comment: true,
attachComment: true,

// create a top-level tokens array containing all tokens
tokens: true,

// The version of ECMAScript syntax to use
ecmaVersion: 9,

// Type of script to parse
sourceType: 'module',

ecmaFeatures: {
}
};
/**
* @link https://github.com/eslint/espree#options
*/
function getAstDefaultOptions() {
return {
/** attach range information to each node */
range: true,

/** attach line/column location information to each node */
loc: true,

/** create a top-level comments array containing all comments */
comment: true,

/** create a top-level tokens array containing all tokens */
tokens: true,

/**
* Set to 3, 5 (default), 6, 7, 8, 9, 10, 11, or 12 to specify
* the version of ECMAScript syntax you want to use.
*
* You can also set to 2015 (same as 6), 2016 (same as 7),
* 2017 (same as 8), 2018 (same as 9), 2019 (same as 10),
* 2020 (same as 11), or 2021 (same as 12) to use the year-based naming.
*/
ecmaVersion: 9,

/** specify which type of script you're parsing ("script" or "module") */
sourceType: 'module',

/** specify additional language features */
ecmaFeatures: {}
};
}

const SUPPORTED_FEATURES = [
'name',
Expand All @@ -46,25 +59,32 @@ const SUPPORTED_FEATURES = [
'refs',
'store'
];
const isFeatureSupported = (v) => SUPPORTED_FEATURES.includes(v);

const INFO_FEATURES_SUPPORTED = `Supported features: ${JSON.stringify(SUPPORTED_FEATURES)}`;

function getUnsupporteFeaturesString(arr) {
return `Features [${utils.printArray(arr)}] in ` +
'options.features are not supported by the SvelteV2 Parser. ' +
INFO_FEATURES_SUPPORTED;
}

const EVENT_EMIT_RE = /\bfire\s*\(\s*((?:'[^']*')|(?:"[^"]*")|(?:`[^`]*`))/;

class Parser extends EventEmitter {
constructor(options) {
options = Object.assign({}, DEFAULT_OPTIONS, options);
super();

Parser.validateOptions(options);

super();

this.source = options.source;
this.features = options.features || SUPPORTED_FEATURES;
this.features = options.features;

if (hasOwnProperty(options.source, 'script') && options.source.script) {
this.scriptOffset = options.source.scriptOffset || 0;

try {
this.ast = espree.parse(options.source.script, options);
this.ast = espree.parse(options.source.script, options.ast);

this.sourceCode = new eslint.SourceCode({
text: options.source.script,
Expand All @@ -73,11 +93,14 @@ class Parser extends EventEmitter {
} catch (e) {
const script = utils.escapeImportKeyword(options.source.script);

this.ast = espree.parse(script, Object.assign({}, options, {
const newAstOptions = {
...options.ast,
loc: true,
range: true,
comment: true
}));
};

this.ast = espree.parse(script, newAstOptions);

this.sourceCode = new eslint.SourceCode({
text: script,
Expand All @@ -91,31 +114,86 @@ class Parser extends EventEmitter {
}

this.includeSourceLocations = options.includeSourceLocations;
this.componentName = null;
this.componentName = options.componentName;
this.template = options.source.template;
this.filename = options.filename;
this.eventsEmmited = {};
this.eventsEmmited = options.eventsEmmited;
this.defaultMethodVisibility = options.defaultMethodVisibility;
this.defaultActionVisibility = options.defaultActionVisibility;
this.identifiers = {};
this.imports = {};
this.identifiers = options.identifiers;
this.imports = options.imports;
}

static getDefaultOptions() {
return {
includeSourceLocations: true,
defaultMethodVisibility: utils.DEFAULT_VISIBILITY,
defaultActionVisibility: utils.DEFAULT_VISIBILITY,
features: [...SUPPORTED_FEATURES],
componentName: null,
eventsEmmited: {},
identifiers: {},
imports: {},
ast: getAstDefaultOptions(),
};
}

static normalizeOptions(options) {
const defaults = Parser.getDefaultOptions();

Object.keys(defaults).forEach((optionKey) => {
// If the key was not set by the user, apply default value.
if (!(optionKey in options)) {
options[optionKey] = defaults[optionKey];
}
});
}

static validateOptions(options) {
if (!options.source) {
throw new Error('options.source is required');
Parser.normalizeOptions(options);

// Check the presence and basic format of multiple options
for (const key of ['eventsEmmited', 'identifiers', 'imports']) {
const hasKey = (key in options);

if (!hasKey) {
throw new Error(`options.${key} is required`);
}

const hasCorrectType = typeof options[key] === 'object';

if (!hasCorrectType) {
throw new TypeError(`options.${key} must be of type 'object'`);
}
}

if ('source' in options) {
['script', 'scriptOffset'].forEach(key => {
if (!(key in options.source)) {
throw new TypeError('options.source must have keys \'script\' and \'scriptOffset\'');
}
});
}

if (options.features) {
if ('features' in options) {
if (!Array.isArray(options.features)) {
throw new TypeError('options.features must be an array');
}

options.features.forEach((feature) => {
if (!SUPPORTED_FEATURES.includes(feature)) {
throw new Error(`Unknow '${feature}' feature. Supported features: ` + JSON.stringify(SUPPORTED_FEATURES));
}
});
if (options.features.length === 0) {
throw new Error(
'options.features must contain at least one feature. ' +
INFO_FEATURES_SUPPORTED
);
}

if (!options.features.every(isFeatureSupported)) {
const notSupported = options.features.filter(
(iv) => !isFeatureSupported(iv)
);

throw new Error(getUnsupporteFeaturesString(notSupported));
}
}
}

Expand Down Expand Up @@ -379,19 +457,15 @@ class Parser extends EventEmitter {
}

internalWalk() {
if (this.features.length === 0) {
return this.emit('end');
}

if (this.template) {
this.parseTemplate();
}

if (this.ast === null) {
if (this.features.includes('name')) {
this.parseComponentName();
}
if (this.features.includes('name')) {
this.parseComponentName();
}

if (this.ast === null) {
return this.emit('end');
}

Expand Down Expand Up @@ -450,10 +524,6 @@ class Parser extends EventEmitter {
} else if (body.expression !== null && body.expression.right && body.expression.right.properties) {
body.expression.right.properties.forEach((property) => this.extractProperties(property));
}

if (this.features.includes('name')) {
this.parseComponentName();
}
});

this.emit('end');
Expand Down

0 comments on commit 5e2ef37

Please sign in to comment.