Skip to content

Commit

Permalink
Quote module names if they are invalid identifiers (#63)
Browse files Browse the repository at this point in the history
Fixes #61.
  • Loading branch information
englercj authored Feb 3, 2019
1 parent ea2d602 commit 1578f22
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 1 deletion.
49 changes: 48 additions & 1 deletion src/create_helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,48 @@ const declareModifier = ts.createModifier(ts.SyntaxKind.DeclareKeyword);
const constModifier = ts.createModifier(ts.SyntaxKind.ConstKeyword);
const readonlyModifier = ts.createModifier(ts.SyntaxKind.ReadonlyKeyword);

// A simplified version of the rules found at:
// https://www.ecma-international.org/ecma-262/5.1/#sec-7.6
//
// Here, an identifier is defined as:
// - Must only contain: `a-z`, `A-Z`, `0-9`, `$`, and `_`
// - Must not start with a number.
// - Must not match a reserved word.
//
const rgxIdentifier = /^[a-zA-Z\$_][a-zA-Z0-9\$_]+$/;
const reservedWords = [
// Keywords
'break', 'do', 'instanceof', 'typeof',
'case', 'else', 'new', 'var',
'catch', 'finally', 'return', 'void',
'continue', 'for', 'switch', 'while',
'debugger', 'function', 'this', 'with',
'default', 'if', 'throw',
'delete', 'in', 'try',

// Future Reserved Words
'class', 'enum', 'extends', 'super',
'const', 'export', 'import',

// Future Reserved Words (strict mode)
'implements', 'let', 'private', 'public', 'yield',
'interface', 'package', 'protected', 'static',

// Null & Boolean literals
'null', 'true', 'false',
];

function isValidIdentifier(name: string)
{
if (!name || !rgxIdentifier.test(name))
return false;

if (reservedWords.indexOf(name) !== -1)
return false;

return true;
}

function validateClassLikeChildren(children: ts.Node[] | undefined, validate: (n: ts.Node) => boolean, msg: string)
{
// Validate that the children array actually contains type elements.
Expand Down Expand Up @@ -336,7 +378,12 @@ export function createModule(doclet: INamespaceDoclet, nested: boolean, children
body = ts.createModuleBlock(children as ts.Statement[]);
}

const name = ts.createIdentifier(doclet.name);
let name;

if (isValidIdentifier(doclet.name))
name = ts.createIdentifier(doclet.name)
else
name = ts.createStringLiteral(doclet.name);

return handleComment(doclet, ts.createModuleDeclaration(
undefined, // decorators
Expand Down
30 changes: 30 additions & 0 deletions test/expected/module.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/** @module array-to-object-keys
*/
declare module "array-to-object-keys" {
/**
* @typedef valueGenerator
* @type {function}
* @param {string} value The original array entry
* @param {number} index The index of the array entry (starts at 0)
* @returns {*}
*/
type valueGenerator = (value: string, index: number) => any;
/**
* Converts an array to an object with static keys and customizable values
* @example
* arrayToObjectKeys(["a", "b"])
* // {a: null, b: null}
* @example
* arrayToObjectKeys(["a", "b"], "value")
* // {a: "value", b: "value"}
* @example
* arrayToObjectKeys(["a", "b"], (key, index) => `value for ${key} #${index + 1}`)
* // {a: "value for a #1", b: "value for b #2"}
* @param {string[]} array Keys for the generated object
* @param {valueGenerator|*} [valueGenerator=null] Optional function that sets the object values based on key and index
* @returns {Object<string, *>} A generated object based on the array input
*/
function arrayToObjectKeys(array: string[], valueGenerator?: valueGenerator | any): {
[key: string]: any;
};
}
29 changes: 29 additions & 0 deletions test/fixtures/module.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/** @module array-to-object-keys */

/**
* @typedef valueGenerator
* @type {function}
* @param {string} value The original array entry
* @param {number} index The index of the array entry (starts at 0)
* @returns {*}
*/

/**
* Converts an array to an object with static keys and customizable values
* @example
* arrayToObjectKeys(["a", "b"])
* // {a: null, b: null}
* @example
* arrayToObjectKeys(["a", "b"], "value")
* // {a: "value", b: "value"}
* @example
* arrayToObjectKeys(["a", "b"], (key, index) => `value for ${key} #${index + 1}`)
* // {a: "value for a #1", b: "value for b #2"}
* @param {string[]} array Keys for the generated object
* @param {valueGenerator|*} [valueGenerator=null] Optional function that sets the object values based on key and index
* @returns {Object<string, *>} A generated object based on the array input
*/
const arrayToObjectKeys = (array, valueGenerator = null) => {
}

export default arrayToObjectKeys
7 changes: 7 additions & 0 deletions test/specs/module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { expectJsDoc } from '../lib';

suite('Module Checks', () => {
test('All', () => {
expectJsDoc('module');
});
});

0 comments on commit 1578f22

Please sign in to comment.