Repository: styleguidist/react-styleguidist (ts)

Issue:
Examples don't support named imports with trailing comma
Hi! I'm in the process of upgrading to version `9.0.1` and encountered this odd issue.

**Current behavior**

An example importing multiple exports from the same file while using a trailing comma in the import statement will break the example.

 ```
import {
  Button,
  Placeholder,
} from 'rsg-example/components';
```

Chrome produces the following error:

`SyntaxError: Unexpected token =`

Firefox produces the following error:

`SyntaxError: missing variable name`

**To reproduce**

I modified the [basic example in a branch on my fork](https://github.com/wkillerud/react-styleguidist/tree/trailing-comma-in-import-breaks-example/examples/basic/src/components) to demonstrate the problem scenario. The [diff is here](https://github.com/wkillerud/react-styleguidist/commit/97b3f0f40f902e19baf20a288afce3beb7387103).

I put a breakpoint in `ReactExample.getExampleComponent` and see the problem in the `compiledCode`:

```
"const react$0 = require('react');
const React = react$0.default || react$0;
const components$0 = require('rsg-example/components');
const Button = components$0.Button;
const Placeholder = components$0.Placeholder;
const  = components$0.;
;React.createElement( Button, null,
  React.createElement( Placeholder, null )
)"
```

`const  = components$0.;`

Removing the trailing comma produces the expected result.

**Expected behavior**

Trailing commas in import statements should not break examples. Some linters and Prettier configs enforce this trailing comma when the number of imports goes above a certain threshold, even in Markdown files (Pretties does this in my current project). 


Pull request description:
Allow trailing comma in named imports in examples
Fixes #1295 

Patch:
diff --git a/.github/Contributing.md b/.github/Contributing.md
index 0808ba6f..c8103f50 100644
--- a/.github/Contributing.md
+++ b/.github/Contributing.md
@@ -14,10 +14,16 @@ If you want to contribute but don’t know what to do, take a look at these two
 
 ## Development workflow
 
-Run Babel in watch mode and start example style guide:
+Run Babel in watch mode:
 
 ```bash
-npm run compile:watch & npm start
+npm run compile:watch
+```
+
+Then open a new terminal and start an example style guide:
+
+```bash
+npm start
 ```
 
 Open [localhost:6060](http://localhost:6060) in a browser.
@@ -54,13 +60,19 @@ First install dependencies:
 npm run test:cypress:pre
 ```
 
-Then start Styleguidist server:
+Then run Babel in watch mode:
+
+```bash
+npm run compile:watch
+```
+
+Then open a new terminal and start Styleguidist server:
 
 ```bash
-npm run compile:watch & npm run test:cypress:startServer
+npm run test:cypress:startServer
 ```
 
-And, finally, in a separate terminal run tests:
+And, finally, in another separate terminal run tests:
 
 ```bash
 npm run test:cypress:run
@@ -78,11 +90,11 @@ npm run test:cypress:open
 - We’re using [Prettier](https://github.com/prettier/prettier) to format JavaScript, so don’t worry much about code formatting.
 - Don’t commit generated files, like minified JavaScript.
 - Don’t change version number and change log.
-- If you're updating examples other then `examples/basic`, you'll need to modify your watch and start commands:
+- If you're updating examples other then `examples/basic`, you'll need to modify your start commands:
 
 ```bash
-npm run compile:watch & npm run start:customised # if making changes to examples/customised
-npm run compile:watch & npm run start:sections # if making changes to examples/sections
+npm run start:customised # if making changes to examples/customised
+npm run start:sections # if making changes to examples/sections
 ```
 
 See the `scripts` section of the top level [package.json](https://github.com/styleguidist/react-styleguidist/blob/master/package.json#L135)
diff --git a/docs/Development.md b/docs/Development.md
index 1a5f60f9..99934798 100644
--- a/docs/Development.md
+++ b/docs/Development.md
@@ -40,7 +40,7 @@ We use webpack loaders to hot reload the style guide on changes in user componen
 
 Styleguidist tries to load and reuse user’s webpack config (`webpack.config.js` in project root folder). It works most of the time but has some restrictions: Styleguidist [ignores](https://github.com/styleguidist/react-styleguidist/blob/master/src/scripts/utils/mergeWebpackConfig.js) some fields and plugins because they are already included (like `webpack.HotModuleReplacementPlugin`), don’t make sense for a style guide (like `output`) or may break Styleguidist (like `entry`).
 
-We’re trying to keep Styleguidist’s own [webpack config](https://github.com/styleguidist/react-styleguidist/blob/master/scripts/make-webpack-config.js) minimal to reduce clashes with user’s configuration.
+We’re trying to keep Styleguidist’s own [webpack config](https://github.com/styleguidist/react-styleguidist/blob/master/src/scripts/make-webpack-config.js) minimal to reduce clashes with user’s configuration.
 
 ## React components
 
@@ -96,11 +96,13 @@ export const styles = ({ fontFamily, fontSize, color }) => ({
   }
 })
 
-export function ExamplePlaceholderRenderer({ classes }) {
+function ExamplePlaceholderRenderer({ classes }) {
   return (
     <button className={classes.button}>I am a styled button</button>
   )
 }
+
+export default Styled(styles)(ExamplePlaceholderRenderer)
 ```
 
 Check available theme variables in [src/client/styles/theme.js](https://github.com/styleguidist/react-styleguidist/blob/master/src/client/styles/theme.js).
diff --git a/package.json b/package.json
index 9af7b65f..a6d71549 100644
--- a/package.json
+++ b/package.json
@@ -15,7 +15,7 @@
     "url": "https://github.com/styleguidist/react-styleguidist/issues"
   },
   "license": "MIT",
-  "main": "scripts/index.js",
+  "main": "lib/scripts/index.js",
   "bin": {
     "styleguidist": "lib/bin/styleguidist.js"
   },
diff --git a/src/bin/styleguidist.js b/src/bin/styleguidist.js
index dcef6c46..0aa52c54 100755
--- a/src/bin/styleguidist.js
+++ b/src/bin/styleguidist.js
@@ -49,12 +49,8 @@ try {
 	config = getConfig(argv.config, updateConfig);
 } catch (err) {
 	if (err instanceof StyleguidistError) {
-		const link = consts.DOCS_CONFIG + (err.anchor ? `#${err.anchor.toLowerCase()}` : '');
-		printErrorWithLink(
-			err.message,
-			`${err.extra}\n\nLearn how to configure your style guide:`,
-			link
-		);
+		const link = consts.DOCS_CONFIG + (err.extra ? `#${err.extra.toLowerCase()}` : '');
+		printErrorWithLink(err.message, `Learn how to configure your style guide:`, link);
 		process.exit(1);
 	} else {
 		throw err;
diff --git a/src/client/utils/rewriteImports.js b/src/client/utils/rewriteImports.js
index 76f1acd1..139e10d5 100644
--- a/src/client/utils/rewriteImports.js
+++ b/src/client/utils/rewriteImports.js
@@ -45,7 +45,7 @@ export default function(str, fn = 'require') {
 	num = 0;
 	return str
 		.replace(NAMED, (_, asterisk, base, req, dep) =>
-			generate(req ? req.split(',') : [], dep, base, fn)
+			generate(req ? req.split(',').filter(dep => dep.trim()) : [], dep, base, fn)
 		)
 		.replace(UNNAMED, (_, dep) => `${fn}('${dep}');`);
 }
diff --git a/src/loaders/utils/getComponentFiles.js b/src/loaders/utils/getComponentFiles.js
index a54145af..d15dbca3 100644
--- a/src/loaders/utils/getComponentFiles.js
+++ b/src/loaders/utils/getComponentFiles.js
@@ -1,4 +1,5 @@
 const glob = require('glob');
+const path = require('path');
 const isFunction = require('lodash/isFunction');
 const isString = require('lodash/isString');
 
@@ -46,6 +47,9 @@ module.exports = function getComponentFiles(components, rootDir, ignore) {
 
 	// Resolve list of components from globs
 	const componentFiles = getFilesMatchingGlobs(componentGlobs, rootDir, ignore);
+	
+	// Get absolute component file paths with correct slash separator format
+	const resolvedComponentFiles = componentFiles.map(file => path.resolve(file));
 
-	return componentFiles;
+	return resolvedComponentFiles;
 };
diff --git a/src/scripts/config.js b/src/scripts/config.js
index 1bd0d750..3b5dbf63 100644
--- a/src/scripts/config.js
+++ b/src/scripts/config.js
@@ -46,8 +46,7 @@ function getConfig(config, update) {
 	} catch (exception) {
 		if (exception instanceof StyleguidistError) {
 			throw new StyleguidistError(
-				'Something is wrong with your style guide config',
-				exception.message,
+				`Something is wrong with your style guide config\n\n${exception.message}`,
 				exception.extra
 			);
 		} else {
diff --git a/src/scripts/utils/error.js b/src/scripts/utils/error.js
index e404649d..5f3ff01c 100644
--- a/src/scripts/utils/error.js
+++ b/src/scripts/utils/error.js
@@ -1,5 +1,5 @@
 class StyleguidistError extends Error {
-	constructor(message, extra, anchor) {
+	constructor(message, extra) {
 		super(message);
 		Error.captureStackTrace(this, this.constructor);
 		Object.defineProperty(this, 'name', {
@@ -8,9 +8,6 @@ class StyleguidistError extends Error {
 		Object.defineProperty(this, 'extra', {
 			value: extra,
 		});
-		Object.defineProperty(this, 'anchor', {
-			value: anchor,
-		});
 	}
 }
 
diff --git a/src/scripts/utils/sanitizeConfig.js b/src/scripts/utils/sanitizeConfig.js
index 5627ab8b..490ad9cf 100644
--- a/src/scripts/utils/sanitizeConfig.js
+++ b/src/scripts/utils/sanitizeConfig.js
@@ -59,7 +59,8 @@ module.exports = function sanitizeConfig(config, schema, rootDir) {
 			throw new StyleguidistError(
 				`Unknown config option ${kleur.bold(key)} was found, the value is:\n` +
 					stringify(value) +
-					(suggestion ? `\n\nDid you mean ${kleur.bold(suggestion)}?` : '')
+					(suggestion ? `\n\nDid you mean ${kleur.bold(suggestion)}?` : ''),
+				suggestion
 			);
 		}
 	});
@@ -98,7 +99,9 @@ module.exports = function sanitizeConfig(config, schema, rootDir) {
 			// Check type
 			const hasRightType = types.some(type => {
 				if (!typeCheckers[type]) {
-					throw Error(`Wrong type ${kleur.bold(type)} specified for ${kleur.bold(key)} in schema.`);
+					throw new StyleguidistError(
+						`Wrong type ${kleur.bold(type)} specified for ${kleur.bold(key)} in schema.`
+					);
 				}
 				return typeCheckers[type](value);
 			});
@@ -108,17 +111,17 @@ module.exports = function sanitizeConfig(config, schema, rootDir) {
 				if (exampleValue) {
 					example[key] = exampleValue;
 				}
-				throw new StyleguidistError(
-					`${kleur.bold(key)} config option should be ${typesList(types)}, received ${typeDetect(
-						value
-					)}.\n` +
-						(exampleValue
-							? `
+				const exampleText = exampleValue
+					? `
 Example:
 
 ${stringify(example)}`
-							: '',
-						key)
+					: '';
+				throw new StyleguidistError(
+					`${kleur.bold(key)} config option should be ${typesList(types)}, received ${typeDetect(
+						value
+					)}.\n${exampleText}`,
+					key
 				);
 			}
 


Test patch:
diff --git a/src/client/utils/__tests__/transpileImports.spec.js b/src/client/utils/__tests__/transpileImports.spec.js
index 6fa8be48..c8e3f200 100644
--- a/src/client/utils/__tests__/transpileImports.spec.js
+++ b/src/client/utils/__tests__/transpileImports.spec.js
@@ -57,6 +57,34 @@ const E = snake$0.default || snake$0;
 `);
 	});
 
+	test('transpile multiline named imports without trailing comma', () => {
+		const result = transpileImports(`import {
+  B,
+  C
+} from 'cat'
+`);
+		expect(result).toMatchInlineSnapshot(`
+"const cat$0 = require('cat');
+const B = cat$0.B;
+const C = cat$0.C;
+"
+`);
+	});
+
+	test('transpile multiline named imports with trailing comma', () => {
+		const result = transpileImports(`import {
+  B,
+  C,
+} from 'cat'
+`);
+		expect(result).toMatchInlineSnapshot(`
+"const cat$0 = require('cat');
+const B = cat$0.B;
+const C = cat$0.C;
+"
+`);
+	});
+
 	test('return code if there are no imports', () => {
 		const code = `<Button />`;
 		const result = transpileImports(code);
diff --git a/src/scripts/__tests__/config.spec.js b/src/scripts/__tests__/config.spec.js
index 69988ddc..e264cfdc 100644
--- a/src/scripts/__tests__/config.spec.js
+++ b/src/scripts/__tests__/config.spec.js
@@ -46,7 +46,7 @@ it('should throw if config has errors', () => {
 			components: 42,
 		});
 	} catch (err) {
-		expect(err.extra).toMatch('should be string, function, or array');
+		expect(err.message).toMatch('should be string, function, or array');
 	}
 });
 
@@ -160,7 +160,7 @@ it('should throw if defaultExample does not exist', () => {
 			defaultExample: 'pizza',
 		});
 	} catch (err) {
-		expect(err.extra).toMatch('does not exist');
+		expect(err.message).toMatch('does not exist');
 	}
 });
 
@@ -226,7 +226,7 @@ it('should throw when old template as a string option passed', () => {
 			template: 'pizza',
 		});
 	} catch (err) {
-		expect(err.extra).toMatch('format has been changed');
+		expect(err.message).toMatch('format has been changed');
 	}
 });
 
@@ -237,7 +237,7 @@ it('should throw when editorConfig option passed', () => {
 			editorConfig: { theme: 'foo' },
 		});
 	} catch (err) {
-		expect(err.extra).toMatch('config option was removed');
+		expect(err.message).toMatch('config option was removed');
 	}
 });