diff --git a/docs/designers-developers/developers/data/data-core-keyboard-shortcuts.md b/docs/designers-developers/developers/data/data-core-keyboard-shortcuts.md
index fefa8e189e2150..4d41954e24fc1c 100644
--- a/docs/designers-developers/developers/data/data-core-keyboard-shortcuts.md
+++ b/docs/designers-developers/developers/data/data-core-keyboard-shortcuts.md
@@ -79,7 +79,7 @@ _Parameters_
- _state_ `Object`: Global state.
- _name_ `string`: Shortcut name.
-- _representation_ (unknown type): Type of representation (display, raw, ariaLabel).
+- _representation_ `keyof FORMATTING_METHODS`: Type of representation (display, raw, ariaLabel).
_Returns_
diff --git a/package-lock.json b/package-lock.json
index cfa78b4f4fc03f..86694410cb7973 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10066,7 +10066,8 @@
"dev": true,
"requires": {
"@babel/core": "^7.9.0",
- "doctrine": "^2.1.0",
+ "comment-parser": "0.7.2",
+ "jsdoctypeparser": "6.1.0",
"lodash": "^4.17.15",
"mdast-util-inject": "1.1.0",
"optionator": "0.8.2",
diff --git a/packages/block-editor/README.md b/packages/block-editor/README.md
index 48f4fad4bdff63..28db8caff2bae3 100644
--- a/packages/block-editor/README.md
+++ b/packages/block-editor/README.md
@@ -296,7 +296,7 @@ _Returns_
# **getFontSize**
Returns the font size object based on an array of named font sizes and the namedFontSize and customFontSize values.
- If namedFontSize is undefined or not found in fontSizes an object with just the size value based on customFontSize is returned.
+If namedFontSize is undefined or not found in fontSizes an object with just the size value based on customFontSize is returned.
_Parameters_
diff --git a/packages/docgen/package.json b/packages/docgen/package.json
index 8ac42fb05336ac..3c419824cb5d90 100644
--- a/packages/docgen/package.json
+++ b/packages/docgen/package.json
@@ -23,7 +23,8 @@
},
"dependencies": {
"@babel/core": "^7.9.0",
- "doctrine": "^2.1.0",
+ "comment-parser": "0.7.2",
+ "jsdoctypeparser": "6.1.0",
"lodash": "^4.17.15",
"mdast-util-inject": "1.1.0",
"optionator": "0.8.2",
diff --git a/packages/docgen/src/get-jsdoc-from-token.js b/packages/docgen/src/get-jsdoc-from-token.js
index e9cbc361c3bc52..8a52c7bc31fc5f 100644
--- a/packages/docgen/src/get-jsdoc-from-token.js
+++ b/packages/docgen/src/get-jsdoc-from-token.js
@@ -1,7 +1,7 @@
/**
* External dependencies.
*/
-const doctrine = require( 'doctrine' );
+const parse = require( 'comment-parser' );
/**
* Internal dependencies.
@@ -19,19 +19,138 @@ const getTypeAsString = require( './get-type-as-string' );
*/
module.exports = function( token ) {
let jsdoc;
- const comments = getLeadingComments( token );
+ let comments = getLeadingComments( token );
if ( comments && /^\*\r?\n/.test( comments ) ) {
- jsdoc = doctrine.parse( comments, {
- unwrap: true,
- recoverable: true,
- sloppy: true,
- } );
- jsdoc.tags = jsdoc.tags.map( ( tag ) => {
- if ( tag.type ) {
- tag.type = getTypeAsString( tag.type );
+ comments = encodeWhitespacesInCode( comments );
+
+ // babel strips /* and */, but comment-parser requires it.
+ jsdoc = parse( `/*${ comments }\n*/` )[ 0 ];
+
+ jsdoc.description = decodeWhitespacesInCode( jsdoc.description );
+
+ delete jsdoc.line;
+ delete jsdoc.source;
+
+ jsdoc.tags = jsdoc.tags.map(
+ ( { tag: title, name, type, description, optional } ) => {
+ const mergeNameAndDesc = () =>
+ `${ name } ${ description }`.trim();
+
+ if ( title === 'deprecated' ) {
+ return {
+ title,
+ description: mergeNameAndDesc(),
+ };
+ }
+
+ if ( title === 'see' ) {
+ return {
+ title,
+ description:
+ name.match( /https?:\/\// ) !== null
+ ? `${ name }\n\n${ description }`.trim()
+ : mergeNameAndDesc(),
+ };
+ }
+
+ if ( title === 'since' ) {
+ return {
+ title,
+ version: name,
+ description,
+ };
+ }
+
+ if ( title === 'param' ) {
+ return {
+ title,
+ name,
+ description,
+ type: getTypeAsString( type, optional ),
+ };
+ }
+
+ if ( title === 'return' ) {
+ return {
+ title,
+ description: mergeNameAndDesc(),
+ type: getTypeAsString( type, optional ),
+ };
+ }
+
+ if ( title === 'type' ) {
+ return {
+ title,
+ description: mergeNameAndDesc(),
+ type: getTypeAsString( type, optional ),
+ };
+ }
+
+ if ( title === 'example' ) {
+ if ( name.match( /```.*/ ) !== null ) {
+ description = `${ name }\n${ description }`.trim();
+ } else if ( name.match( /
.*/ ) !== null ) {
+ // do nothing
+ } else {
+ description = mergeNameAndDesc();
+ }
+
+ return {
+ title,
+ description: decodeWhitespacesInCode( description ),
+ };
+ }
+
+ if ( title === 'typedef' ) {
+ return {
+ title,
+ name,
+ type: getTypeAsString( type, optional ),
+ description,
+ };
+ }
+
+ if ( title === 'property' ) {
+ return {
+ title,
+ name,
+ type: getTypeAsString( type, optional ),
+ description,
+ };
+ }
+
+ return {
+ title,
+ description,
+ };
}
- return tag;
- } );
+ );
}
return jsdoc;
};
+
+const codeRegex = /```.*\n[\s\S]*```/g;
+const CODE_TAB = '__CODE_TAB__';
+const CODE_SPACE = '__CODE_SPACE__';
+
+const encodeWhitespacesInCode = ( comments ) => {
+ return comments.replace( codeRegex, ( m0 ) => {
+ return m0.replace( /\n \*[ \t][ \t]+/g, ( m1 ) => {
+ return (
+ '\n * ' +
+ m1
+ .substring( 4 )
+ .replace( /\t/g, CODE_TAB )
+ .replace( / /g, CODE_SPACE )
+ );
+ } );
+ } );
+};
+
+const decodeWhitespacesInCode = ( description ) => {
+ return description.replace( codeRegex, ( m0 ) => {
+ return m0
+ .replace( new RegExp( CODE_TAB, 'g' ), '\t' )
+ .replace( new RegExp( CODE_SPACE, 'g' ), ' ' );
+ } );
+};
diff --git a/packages/docgen/src/get-type-as-string.js b/packages/docgen/src/get-type-as-string.js
index 6022ded3e90320..1f9b29519f0e18 100644
--- a/packages/docgen/src/get-type-as-string.js
+++ b/packages/docgen/src/get-type-as-string.js
@@ -1,47 +1,63 @@
-const maybeAddDefault = function( value, defaultValue ) {
- if ( defaultValue ) {
- return `value=${ defaultValue }`;
+const { parse } = require( 'jsdoctypeparser' );
+
+const getType = ( ast, typeString, noUnionParenthesis ) => {
+ if ( ast.type === 'NAME' ) {
+ return ast.name;
+ }
+
+ if ( ast.type === 'ANY' ) {
+ return '*';
+ }
+
+ if ( ast.type === 'GENERIC' ) {
+ const types = ast.objects.map( ( o ) => getType( o ) ).join( ',' );
+ return `${ getType( ast.subject ) }<${ types }>`;
}
- return value;
-};
-const getType = function( param, defaultValue ) {
- if ( ! defaultValue ) {
- defaultValue = param.default;
- }
-
- if ( param.type.type ) {
- return getType( param.type, defaultValue );
- } else if ( param.expression ) {
- if ( param.type === 'RestType' ) {
- return `...${ getType( param.expression, defaultValue ) }`;
- } else if ( param.type === 'NullableType' ) {
- return `?${ getType( param.expression, defaultValue ) }`;
- } else if ( param.type === 'TypeApplication' ) {
- return `${ getType(
- param.expression,
- defaultValue
- ) }<${ param.applications
- .map( ( application ) => getType( application ) )
- .join( ',' ) }>`;
- } else if ( param.type === 'OptionalType' ) {
- return `[${ getType( param.expression, defaultValue ) }]`;
- }
- return getType( param.expression, defaultValue );
- } else if ( param.elements ) {
- const types = param.elements.map( ( element ) => getType( element ) );
- return maybeAddDefault( `(${ types.join( '|' ) })`, defaultValue );
- } else if ( param.type === 'AllLiteral' ) {
- return maybeAddDefault( '*', defaultValue );
- } else if ( param.type === 'NullLiteral' ) {
- return maybeAddDefault( 'null', defaultValue );
- } else if ( param.type === 'UndefinedLiteral' ) {
- return maybeAddDefault( 'undefined', defaultValue );
- }
-
- return maybeAddDefault( param.name, defaultValue );
+ if ( ast.type === 'NULLABLE' ) {
+ return `?${ getType( ast.value ) }`;
+ }
+
+ if ( ast.type === 'VARIADIC' ) {
+ return `...${ getType( ast.value ) }`;
+ }
+
+ if ( ast.type === 'UNION' ) {
+ const left = getType( ast.left );
+ const right = getType( ast.right, null, true );
+ const type = `${ left }|${ right }`;
+
+ return noUnionParenthesis ? type : `(${ type })`;
+ }
+
+ if ( ast.type === 'PARENTHESIS' ) {
+ const type = getType( ast.value );
+ return type[ 0 ] === '(' && type[ type.length - 1 ] === ')'
+ ? type
+ : `(${ type })`;
+ }
+
+ if ( ast.type === 'OPTIONAL' ) {
+ return `[${ getType( ast.value ) }]`;
+ }
+
+ if ( ast.type === 'MEMBER' && ast.owner.type === 'IMPORT' ) {
+ return `${ ast.name }`;
+ }
+
+ return typeString || 'unknown type';
};
-module.exports = function( param ) {
- return getType( param );
+module.exports = function( typeString, optional ) {
+ let ast;
+
+ try {
+ ast = parse( typeString );
+ } catch {
+ return 'unknown type';
+ }
+
+ const type = getType( ast, typeString );
+
+ return optional ? `[${ type }]` : type;
};
diff --git a/packages/docgen/src/test/fixtures/default-parse-error/exports.json b/packages/docgen/src/test/fixtures/default-parse-error/exports.json
index 04e396cc1fc436..6cf22ba030c1a6 100644
--- a/packages/docgen/src/test/fixtures/default-parse-error/exports.json
+++ b/packages/docgen/src/test/fixtures/default-parse-error/exports.json
@@ -1 +1 @@
-[{"type":"ExportDefaultDeclaration","start":173,"end":288,"loc":{"start":{"line":7,"column":0},"end":{"line":9,"column":1}},"range":[173,288],"declaration":{"type":"FunctionDeclaration","start":188,"end":288,"loc":{"start":{"line":7,"column":15},"end":{"line":9,"column":1}},"range":[188,288],"id":{"type":"Identifier","start":197,"end":221,"loc":{"start":{"line":7,"column":24},"end":{"line":7,"column":48}},"range":[197,221],"name":"invokeCallbackAfterDelay"},"generator":false,"expression":false,"async":false,"params":[{"type":"Identifier","start":223,"end":231,"loc":{"start":{"line":7,"column":50},"end":{"line":7,"column":58}},"range":[223,231],"name":"callback"}],"body":{"type":"BlockStatement","start":234,"end":288,"loc":{"start":{"line":7,"column":61},"end":{"line":9,"column":1}},"range":[234,288],"body":[{"type":"ExpressionStatement","start":237,"end":286,"loc":{"start":{"line":8,"column":1},"end":{"line":8,"column":50}},"range":[237,286],"expression":{"type":"CallExpression","start":237,"end":285,"loc":{"start":{"line":8,"column":1},"end":{"line":8,"column":49}},"range":[237,285],"callee":{"type":"Identifier","start":237,"end":247,"loc":{"start":{"line":8,"column":1},"end":{"line":8,"column":11}},"range":[237,247],"name":"setTimeout"},"arguments":[{"type":"ArrowFunctionExpression","start":249,"end":277,"loc":{"start":{"line":8,"column":13},"end":{"line":8,"column":41}},"range":[249,277],"id":null,"generator":false,"expression":true,"async":false,"params":[],"body":{"type":"CallExpression","start":255,"end":277,"loc":{"start":{"line":8,"column":19},"end":{"line":8,"column":41}},"range":[255,277],"callee":{"type":"Identifier","start":255,"end":263,"loc":{"start":{"line":8,"column":19},"end":{"line":8,"column":27}},"range":[255,263],"name":"callback"},"arguments":[{"type":"CallExpression","start":265,"end":275,"loc":{"start":{"line":8,"column":29},"end":{"line":8,"column":39}},"range":[265,275],"callee":{"type":"MemberExpression","start":265,"end":273,"loc":{"start":{"line":8,"column":29},"end":{"line":8,"column":37}},"range":[265,273],"object":{"type":"Identifier","start":265,"end":269,"loc":{"start":{"line":8,"column":29},"end":{"line":8,"column":33}},"range":[265,269],"name":"Date"},"property":{"type":"Identifier","start":270,"end":273,"loc":{"start":{"line":8,"column":34},"end":{"line":8,"column":37}},"range":[270,273],"name":"now"},"computed":false},"arguments":[]}]}},{"type":"Literal","start":279,"end":283,"loc":{"start":{"line":8,"column":43},"end":{"line":8,"column":47}},"range":[279,283],"value":1000,"raw":"1000"}]}}]}},"leadingComments":[{"type":"Block","value":"*\n * Function invoking callback after delay with current timestamp in milliseconds\n * since epoch.\n *\n * @param {(timestamp:number)=>void} callback Callback function.\n ","start":0,"end":172,"range":[0,172],"loc":{"start":{"line":1,"column":0},"end":{"line":6,"column":3}}}]}]
+[{"type":"ExportDefaultDeclaration","start":173,"end":288,"loc":{"start":{"line":7,"column":0},"end":{"line":9,"column":1}},"range":[173,288],"declaration":{"type":"FunctionDeclaration","start":188,"end":288,"loc":{"start":{"line":7,"column":15},"end":{"line":9,"column":1}},"range":[188,288],"id":{"type":"Identifier","start":197,"end":221,"loc":{"start":{"line":7,"column":24},"end":{"line":7,"column":48}},"range":[197,221],"name":"invokeCallbackAfterDelay"},"generator":false,"expression":false,"async":false,"params":[{"type":"Identifier","start":223,"end":231,"loc":{"start":{"line":7,"column":50},"end":{"line":7,"column":58}},"range":[223,231],"name":"callback"}],"body":{"type":"BlockStatement","start":234,"end":288,"loc":{"start":{"line":7,"column":61},"end":{"line":9,"column":1}},"range":[234,288],"body":[{"type":"ExpressionStatement","start":237,"end":286,"loc":{"start":{"line":8,"column":1},"end":{"line":8,"column":50}},"range":[237,286],"expression":{"type":"CallExpression","start":237,"end":285,"loc":{"start":{"line":8,"column":1},"end":{"line":8,"column":49}},"range":[237,285],"callee":{"type":"Identifier","start":237,"end":247,"loc":{"start":{"line":8,"column":1},"end":{"line":8,"column":11}},"range":[237,247],"name":"setTimeout"},"arguments":[{"type":"ArrowFunctionExpression","start":249,"end":277,"loc":{"start":{"line":8,"column":13},"end":{"line":8,"column":41}},"range":[249,277],"id":null,"generator":false,"expression":true,"async":false,"params":[],"body":{"type":"CallExpression","start":255,"end":277,"loc":{"start":{"line":8,"column":19},"end":{"line":8,"column":41}},"range":[255,277],"callee":{"type":"Identifier","start":255,"end":263,"loc":{"start":{"line":8,"column":19},"end":{"line":8,"column":27}},"range":[255,263],"name":"callback"},"arguments":[{"type":"CallExpression","start":265,"end":275,"loc":{"start":{"line":8,"column":29},"end":{"line":8,"column":39}},"range":[265,275],"callee":{"type":"MemberExpression","start":265,"end":273,"loc":{"start":{"line":8,"column":29},"end":{"line":8,"column":37}},"range":[265,273],"object":{"type":"Identifier","start":265,"end":269,"loc":{"start":{"line":8,"column":29},"end":{"line":8,"column":33}},"range":[265,269],"name":"Date"},"property":{"type":"Identifier","start":270,"end":273,"loc":{"start":{"line":8,"column":34},"end":{"line":8,"column":37}},"range":[270,273],"name":"now"},"computed":false},"arguments":[]}]}},{"type":"Literal","start":279,"end":283,"loc":{"start":{"line":8,"column":43},"end":{"line":8,"column":47}},"range":[279,283],"value":1000,"raw":"1000"}]}}]}},"leadingComments":[{"type":"Block","value":"*\n * Function invoking callback after delay with current timestamp in milliseconds\n * since epoch.\n *\n * @param {A&B} callback Callback function.\n ","start":0,"end":172,"range":[0,172],"loc":{"start":{"line":1,"column":0},"end":{"line":6,"column":3}}}]}]
diff --git a/packages/docgen/src/test/get-intermediate-representation.js b/packages/docgen/src/test/get-intermediate-representation.js
index ab1914dbd397f4..7bd4b23ebbad5c 100644
--- a/packages/docgen/src/test/get-intermediate-representation.js
+++ b/packages/docgen/src/test/get-intermediate-representation.js
@@ -70,10 +70,9 @@ describe( 'Intermediate Representation', function() {
tags: [
{
description: 'Callback function.',
- errors: [ 'unexpected token' ],
name: 'callback',
title: 'param',
- type: null,
+ type: 'unknown type',
},
],
lineStart: 7,
diff --git a/packages/docgen/src/test/get-jsdoc-from-token.js b/packages/docgen/src/test/get-jsdoc-from-token.js
index 3475e69938187e..a1b075e3befee4 100644
--- a/packages/docgen/src/test/get-jsdoc-from-token.js
+++ b/packages/docgen/src/test/get-jsdoc-from-token.js
@@ -3,14 +3,56 @@
*/
const getJSDocFromToken = require( '../get-jsdoc-from-token' );
-describe( 'JSDoc', () => {
- it( 'extracts description and tags', () => {
+const expectToExtractExample = ( code, description ) => {
+ expect(
+ getJSDocFromToken( {
+ leadingComments: [
+ {
+ value: code,
+ },
+ ],
+ } )
+ ).toEqual( {
+ description: '',
+ tags: [
+ {
+ title: 'example',
+ description,
+ },
+ ],
+ } );
+};
+
+describe( 'Parse JSDoc and extract description and tags', () => {
+ it( 'Normal definition', () => {
+ const code = `
+ *
+ * A function that adds two parameters.
+ *
+ * @deprecated Use native addition instead.
+ * @since v2
+ *
+ * @see addition
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators
+ *
+ * @param {number} firstParam The first param to add.
+ * @param {number} secondParam The second param to add.
+ *
+ * @example
+ *
+ * \`\`\`js
+ * const addResult = sum( 1, 3 );
+ * console.log( addResult ); // will yield 4
+ * \`\`\`
+ *
+ * @return {number} The result of adding the two params.
+`.trim();
+
expect(
getJSDocFromToken( {
leadingComments: [
{
- value:
- '*\n * A function that adds two parameters.\n *\n * @deprecated Use native addition instead.\n * @since v2\n *\n * @see addition\n * @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators\n *\n * @param {number} firstParam The first param to add.\n * @param {number} secondParam The second param to add.\n *\n * @example\n *\n * ```js\n * const addResult = sum( 1, 3 );\n * console.log( addResult ); // will yield 4\n * ```\n *\n * @return {number} The result of adding the two params.\n ',
+ value: code,
},
],
} )
@@ -23,14 +65,15 @@ describe( 'JSDoc', () => {
},
{
title: 'since',
- description: 'v2',
+ version: 'v2',
+ description: '',
},
{
title: 'see',
description: 'addition',
},
{
- title: 'link',
+ title: 'see',
description:
'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators',
},
@@ -59,12 +102,42 @@ describe( 'JSDoc', () => {
],
} );
+ // Test spaces in code are preserved.
expect(
getJSDocFromToken( {
leadingComments: [
{
+ // Adapted from packages/compose/src/hooks/use-resize-observer/index.js
value:
- '*\n * Constant to document the meaning of life,\n * the universe and everything else.\n *\n * @type {number}\n ',
+ '*\n * X\n *\n * @example\n *\n * ```\n * const y = (\n * x === 3\n * );\n * ```\n',
+ },
+ ],
+ } )
+ ).toEqual( {
+ description: 'X',
+ tags: [
+ {
+ title: 'example',
+ description: '```\nconst y = (\n x === 3\n);\n```',
+ },
+ ],
+ } );
+ } );
+
+ it( 'variable type', () => {
+ const code = `
+ *
+ * Constant to document the meaning of life,
+ * the universe and everything else.
+ *
+ * @type {number}
+`.trim();
+
+ expect(
+ getJSDocFromToken( {
+ leadingComments: [
+ {
+ value: code,
},
],
} )
@@ -74,18 +147,25 @@ describe( 'JSDoc', () => {
tags: [
{
title: 'type',
- description: null,
+ description: '',
type: 'number',
},
],
} );
+ } );
+
+ it( 'function definition', () => {
+ const code = `
+ *
+ * Function invoking callback after delay with current timestamp in milliseconds since epoch.
+ * @param {(timestamp:number)=>void} callback Callback function.
+`.trim();
expect(
getJSDocFromToken( {
leadingComments: [
{
- value:
- '*\n * Function invoking callback after delay with current timestamp in milliseconds since epoch.\n * @param {(timestamp:number)=>void} callback Callback function.\n ',
+ value: code,
},
],
} )
@@ -95,10 +175,316 @@ describe( 'JSDoc', () => {
tags: [
{
title: 'param',
- errors: [ 'unexpected token' ],
description: 'Callback function.',
name: 'callback',
- type: null,
+ type: '(timestamp:number)=>void',
+ },
+ ],
+ } );
+ } );
+
+ it( 'handles parse failure', () => {
+ const code = `
+ *
+ * WordPress
+ * @param {A&B} callback It's OK in TypeScript. But it's not in JSDoc.
+`.trim();
+
+ expect(
+ getJSDocFromToken( {
+ leadingComments: [
+ {
+ value: code,
+ },
+ ],
+ } )
+ ).toEqual( {
+ description: 'WordPress',
+ tags: [
+ {
+ title: 'param',
+ description: `It's OK in TypeScript. But it's not in JSDoc.`,
+ name: 'callback',
+ type: 'unknown type',
+ },
+ ],
+ } );
+ } );
+
+ it( 'handles code block in description', () => {
+ // Adapted from rich-text/src/create.js
+ const code = `
+ *
+ * Blah blah blah
+ *
+ * \`\`\`js
+ * {
+ * text: string,
+ * }
+ * \`\`\`
+`.trim();
+
+ const description = `
+Blah blah blah
+
+\`\`\`js
+{
+ text: string,
+}
+\`\`\`
+`.trim();
+
+ expect(
+ getJSDocFromToken( {
+ leadingComments: [
+ {
+ value: code,
+ },
+ ],
+ } )
+ ).toEqual( {
+ description,
+ tags: [],
+ } );
+ } );
+
+ it( 'tabs in code example are preserved', () => {
+ // Adapted from packages/compose/src/hooks/use-resize-observer/index.js
+ const code = `
+ *
+ * @example
+ *
+ * \`\`\`js
+ * const App = () => {
+ * let testTab = ' ';
+ * const [ resizeListener, sizes ] = useResizeObserver();
+ *
+ * return (
+ *
+ * { resizeListener }
+ * Your content here
+ *
+ * );
+ * };
+ * \`\`\`
+ *
+`.trim();
+
+ const description = `
+\`\`\`js
+const App = () => {
+ let testTab = ' ';
+ const [ resizeListener, sizes ] = useResizeObserver();
+
+ return (
+
+ { resizeListener }
+ Your content here
+
+ );
+};
+\`\`\`
+`.trim();
+
+ expectToExtractExample( code, description );
+ } );
+
+ it( 'tabs in code example are preserved even when the first char after * is a tab', () => {
+ // Adapted from edit-post/src/components/sidebar/plugin-document-setting-panel/index.js
+ const code = `
+ *
+ * @example
+ * \`\`\`jsx
+ * const MyDocumentSettingTest = () => (
+ *
+ * My Document Setting Panel
+ *
+ * );
+ * \`\`\`
+`.trim();
+
+ const description = `
+\`\`\`jsx
+const MyDocumentSettingTest = () => (
+
+ My Document Setting Panel
+
+);
+\`\`\`
+`.trim();
+
+ expectToExtractExample( code, description );
+ } );
+
+ it( 'spaces in code example are preserved', () => {
+ // Adapted from packages/data/src/components/with-dispatch/index.js
+ const code = `
+ *
+ * @example
+ *
+ * \`\`\`
+ * const y = (
+ * x === 3
+ * );
+ * \`\`\`
+`.trim();
+
+ const description = `
+\`\`\`
+const y = (
+ x === 3
+);
+\`\`\`
+`.trim();
+
+ expectToExtractExample( code, description );
+ } );
+
+ it( 'no empty line after @example tag: ```js', () => {
+ // Adapted from a11y/src/index.js
+ const code = `
+ *
+ * @example
+ * \`\`\`js
+ * import { speak } from '@wordpress/a11y';
+ * \`\`\`
+`.trim();
+
+ const description = `
+\`\`\`js
+import { speak } from '@wordpress/a11y';
+\`\`\`
+`.trim();
+
+ expectToExtractExample( code, description );
+ } );
+
+ it( 'no empty line after @example tag: ```', () => {
+ // Adapted from a11y/src/index.js
+ const code = `
+ *
+ * @example
+ * \`\`\`
+ * ls -a
+ * \`\`\`
+`.trim();
+
+ const description = `
+\`\`\`
+ ls -a
+\`\`\`
+`.trim();
+
+ expectToExtractExample( code, description );
+ } );
+
+ it( 'no empty line after @example tag: normal text', () => {
+ // Adapted from block-serialization-default-parser/src/index.js
+ const code = `
+ *
+ * @example
+ * Input post:
+ * \`\`\`html
+ *
+ * \`\`\`
+ *
+`.trim();
+
+ const description = `
+Input post:
+\`\`\`html
+
+\`\`\`
+`.trim();
+
+ expectToExtractExample( code, description );
+ } );
+
+ it( 'no empty line after @example tag: ', () => {
+ // Adapted from edit-post/src/components/block-settings-menu/plugin-block-settings-menu-items.js
+ const code = `
+ *
+ * @example
+ * ES5
+ * \`\`\`js
+ * // Using ES5 syntax
+ * var __ = wp.i18n.__;
+ * \`\`\`
+`.trim();
+
+ const description = `
+\`\`\`js
+// Using ES5 syntax
+var __ = wp.i18n.__;
+\`\`\`
+`.trim();
+
+ expectToExtractExample( code, description );
+ } );
+
+ it( '@see tag with explanation', () => {
+ const code = `
+ *
+ * @see https://google.com
+ *
+ * Find something here.
+`.trim();
+
+ const description = `
+https://google.com
+
+Find something here.
+`.trim();
+
+ expect(
+ getJSDocFromToken( {
+ leadingComments: [
+ {
+ value: code,
+ },
+ ],
+ } )
+ ).toEqual( {
+ description: '',
+ tags: [
+ {
+ title: 'see',
+ description,
+ },
+ ],
+ } );
+ } );
+
+ it( '@typedef and @property', () => {
+ // Adapted from block-editor/src/store/selectors.js
+ const code = `
+ *
+ * @typedef {Object} WPEditorInserterItem
+ * @property {string} id Unique identifier for the item.
+`.trim();
+
+ expect(
+ getJSDocFromToken( {
+ leadingComments: [
+ {
+ value: code,
+ },
+ ],
+ } )
+ ).toEqual( {
+ description: '',
+ tags: [
+ {
+ title: 'typedef',
+ type: 'Object',
+ name: 'WPEditorInserterItem',
+ description: '',
+ },
+ {
+ title: 'property',
+ name: 'id',
+ type: 'string',
+ description: 'Unique identifier for the item.',
},
],
} );
diff --git a/packages/docgen/src/test/get-type-as-string.js b/packages/docgen/src/test/get-type-as-string.js
index 6ba09acd33df18..c85fb63e77e62f 100644
--- a/packages/docgen/src/test/get-type-as-string.js
+++ b/packages/docgen/src/test/get-type-as-string.js
@@ -5,104 +5,72 @@ const getType = require( '../get-type-as-string' );
describe( 'getType from JSDoc', () => {
it( 'NameExpression', () => {
- const type = getType( {
- title: 'param',
- description: 'description',
- type: {
- type: 'NameExpression',
- name: 'Array',
- },
- name: 'paramName',
- } );
+ const type = getType( 'Array' );
expect( type ).toBe( 'Array' );
} );
it( 'AllLiteral', () => {
- const type = getType( {
- title: 'param',
- description: 'description',
- type: {
- type: 'AllLiteral',
- },
- name: 'paramName',
- } );
+ const type = getType( '*' );
expect( type ).toBe( '*' );
} );
it( 'Applications', () => {
- const type = getType( {
- title: 'param',
- description: 'description',
- type: {
- type: 'TypeApplication',
- expression: {
- type: 'NameExpression',
- name: 'Array',
- },
- applications: [
- {
- type: 'NameExpression',
- name: 'Object',
- },
- {
- type: 'NameExpression',
- name: 'String',
- },
- ],
- },
- } );
- expect( type ).toBe( 'Array