Skip to content
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

Add unicorn linting rules #4666

Merged
merged 1 commit into from Oct 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
21 changes: 18 additions & 3 deletions .eslintrc.js
Expand Up @@ -10,7 +10,8 @@ module.exports = {
'prettier',
'plugin:prettier/recommended',
'plugin:import/recommended',
'plugin:import/typescript'
'plugin:import/typescript',
'plugin:unicorn/recommended'
],
ignorePatterns: [
'node_modules',
Expand All @@ -26,7 +27,9 @@ module.exports = {
{
files: ['*.js'],
rules: {
'@typescript-eslint/explicit-module-boundary-types': 'off'
'@typescript-eslint/explicit-module-boundary-types': 'off',
'unicorn/no-process-exit': 'off',
'unicorn/prefer-module': 'off'
}
},
{
Expand Down Expand Up @@ -94,6 +97,18 @@ module.exports = {
ignoreMemberSort: false
}
],
'sort-keys': ['error', 'asc', { caseSensitive: false }]
'sort-keys': ['error', 'asc', { caseSensitive: false }],
'unicorn/filename-case': 'off',
'unicorn/no-array-callback-reference': 'off',
'unicorn/no-array-reduce': 'off',
'unicorn/no-await-expression-member': 'off',
'unicorn/no-nested-ternary': 'off',
'unicorn/no-null': 'off',
'unicorn/no-this-assignment': 'off',
'unicorn/no-useless-undefined': 'off',
'unicorn/prefer-code-point': 'off',
'unicorn/prefer-math-trunc': 'off',
'unicorn/prefer-number-properties': 'off',
'unicorn/prefer-top-level-await': 'off'
}
};
4 changes: 2 additions & 2 deletions browser/src/error.ts
@@ -1,4 +1,4 @@
import { errNoFileSystemInBrowser, error } from '../../src/utils/error';
import { error, errorNoFileSystemInBrowser } from '../../src/utils/error';

export const throwNoFileSystem = (method: string) => (): never =>
error(errNoFileSystemInBrowser(method));
error(errorNoFileSystemInBrowser(method));
8 changes: 4 additions & 4 deletions browser/src/path.ts
@@ -1,4 +1,4 @@
const ABSOLUTE_PATH_REGEX = /^(?:\/|(?:[A-Za-z]:)?[\\|/])/;
const ABSOLUTE_PATH_REGEX = /^(?:\/|(?:[A-Za-z]:)?[/\\|])/;
const RELATIVE_PATH_REGEX = /^\.?\.\//;
const ALL_BACKSLASHES_REGEX = /\\/g;
const ANY_SLASH_REGEX = /[/\\]/;
Expand All @@ -24,10 +24,10 @@ export function dirname(path: string): string {
const match = /[/\\][^/\\]*$/.exec(path);
if (!match) return '.';

const dir = path.slice(0, -match[0].length);
const directory = path.slice(0, -match[0].length);

// If `dir` is the empty string, we're at root.
return dir ? dir : '/';
// If `directory` is the empty string, we're at root.
return directory || '/';
}

export function extname(path: string): string {
Expand Down
4 changes: 2 additions & 2 deletions build-plugins/clean-before-write.ts
@@ -1,13 +1,13 @@
import fs from 'fs-extra';
import type { Plugin } from 'rollup';

export default function cleanBeforeWrite(dir: string): Plugin {
export default function cleanBeforeWrite(directory: string): Plugin {
let removePromise: Promise<void> | null = null;
return {
generateBundle(_options, _bundle, isWrite) {
if (isWrite) {
// Only remove before first write, but make all writes wait on the removal
removePromise ||= fs.remove(dir);
removePromise ||= fs.remove(directory);
return removePromise;
}
},
Expand Down
16 changes: 8 additions & 8 deletions build-plugins/generate-license-file.ts
Expand Up @@ -4,12 +4,12 @@ import type { PluginImpl } from 'rollup';
import license, { type Dependency, type Person } from 'rollup-plugin-license';

async function generateLicenseFile(
dir: string,
directory: string,
dependencies: readonly Dependency[]
): Promise<void> {
const coreLicense = await fs.readFile('LICENSE-CORE.md', 'utf8');
const licenses = new Set<string>();
const dependencyLicenseTexts = Array.from(dependencies)
const dependencyLicenseTexts = [...dependencies]
.filter(({ name }) => name !== '@rollup/browser')
.sort(({ name: nameA }, { name: nameB }) => (nameA! > nameB! ? 1 : -1))
.map(({ name, license, licenseText, author, maintainers, contributors, repository }) => {
Expand All @@ -22,13 +22,13 @@ async function generateLicenseFile(
names.add(author.name);
}
// TODO there is an inconsistency in the rollup-plugin-license types
for (const person of contributors.concat(maintainers as unknown as Person[])) {
for (const person of [...contributors, ...(maintainers as unknown as Person[])]) {
if (person?.name) {
names.add(person.name);
}
}
if (names.size > 0) {
text += `By: ${Array.from(names).join(', ')}\n`;
text += `By: ${[...names].join(', ')}\n`;
}
if (repository) {
text += `Repository: ${(typeof repository === 'object' && repository.url) || repository}\n`;
Expand All @@ -54,10 +54,10 @@ async function generateLicenseFile(
coreLicense +
`\n# Licenses of bundled dependencies\n` +
`The published Rollup artifact additionally contains code with the following licenses:\n` +
`${Array.from(licenses).join(', ')}\n\n` +
`${[...licenses].join(', ')}\n\n` +
`# Bundled dependencies:\n` +
dependencyLicenseTexts;
const licenseFile = join(dir, 'LICENSE.md');
const licenseFile = join(directory, 'LICENSE.md');
const existingLicenseText = await fs.readFile(licenseFile, 'utf8');
if (existingLicenseText !== licenseText) {
await fs.writeFile(licenseFile, licenseText);
Expand All @@ -70,7 +70,7 @@ interface LicenseHandler {
writeLicense: PluginImpl;
}

export default function getLicenseHandler(dir: string): LicenseHandler {
export default function getLicenseHandler(directory: string): LicenseHandler {
const licenses = new Map<string, Dependency>();
function addLicenses(dependencies: readonly Dependency[]) {
for (const dependency of dependencies) {
Expand All @@ -85,7 +85,7 @@ export default function getLicenseHandler(dir: string): LicenseHandler {
return {
name: 'write-license',
writeBundle() {
return generateLicenseFile(dir, Array.from(licenses.values()));
return generateLicenseFile(directory, [...licenses.values()]);
}
};
}
Expand Down
2 changes: 1 addition & 1 deletion build-plugins/get-banner.ts
Expand Up @@ -27,5 +27,5 @@ export default async function getBanner(): Promise<string> {
return (getBannerPromise ||= Promise.all([
execPromise('git rev-parse HEAD'),
fs.readFile(new URL('../package.json', import.meta.url), 'utf8')
]).then(([{ stdout }, pkg]) => generateBanner(stdout.trim(), JSON.parse(pkg).version)));
]).then(([{ stdout }, package_]) => generateBanner(stdout.trim(), JSON.parse(package_).version)));
}
1 change: 1 addition & 0 deletions cli/cli.ts
Expand Up @@ -16,6 +16,7 @@ if (command.help || (process.argv.length <= 2 && process.stdin.isTTY)) {
console.log(`rollup v${version}`);
} else {
try {
// eslint-disable-next-line unicorn/prefer-module
require('source-map-support').install();
} catch {
// do nothing
Expand Down
32 changes: 17 additions & 15 deletions cli/logging.ts
Expand Up @@ -4,35 +4,37 @@ import { bold, cyan, dim, red } from '../src/utils/colors';
import relativeId from '../src/utils/relativeId';

// log to stderr to keep `rollup main.js > bundle.js` from breaking
export const stderr = (...args: readonly unknown[]) => process.stderr.write(`${args.join('')}\n`);
export const stderr = (...parameters: readonly unknown[]) =>
process.stderr.write(`${parameters.join('')}\n`);

export function handleError(err: RollupError, recover = false): void {
const name = err.name || err.cause?.name;
export function handleError(error: RollupError, recover = false): void {
const name = error.name || error.cause?.name;
const nameSection = name ? `${name}: ` : '';
const pluginSection = err.plugin ? `(plugin ${err.plugin}) ` : '';
const message = `${pluginSection}${nameSection}${err.message}`;
const pluginSection = error.plugin ? `(plugin ${error.plugin}) ` : '';
const message = `${pluginSection}${nameSection}${error.message}`;

stderr(bold(red(`[!] ${bold(message.toString())}`)));

if (err.url) {
stderr(cyan(err.url));
if (error.url) {
stderr(cyan(error.url));
}

if (err.loc) {
stderr(`${relativeId((err.loc.file || err.id)!)} (${err.loc.line}:${err.loc.column})`);
} else if (err.id) {
stderr(relativeId(err.id));
if (error.loc) {
stderr(`${relativeId((error.loc.file || error.id)!)} (${error.loc.line}:${error.loc.column})`);
} else if (error.id) {
stderr(relativeId(error.id));
}

if (err.frame) {
stderr(dim(err.frame));
if (error.frame) {
stderr(dim(error.frame));
}

if (err.stack) {
stderr(dim(err.stack));
if (error.stack) {
stderr(dim(error.stack));
}

stderr('');

// eslint-disable-next-line unicorn/no-process-exit
if (!recover) process.exit(1);
}
10 changes: 5 additions & 5 deletions cli/run/batchWarnings.ts
Expand Up @@ -51,7 +51,7 @@ export default function batchWarnings(): BatchWarnings {
flush() {
if (count === 0) return;

const codes = Array.from(deferredWarnings.keys()).sort(
const codes = [...deferredWarnings.keys()].sort(
(a, b) => deferredWarnings.get(b)!.length - deferredWarnings.get(a)!.length
);

Expand Down Expand Up @@ -245,8 +245,8 @@ const deferredHandlers: {
}
};

function title(str: string): void {
stderr(bold(yellow(`(!) ${str}`)));
function title(string_: string): void {
stderr(bold(yellow(`(!) ${string_}`)));
}

function info(url: string): void {
Expand All @@ -258,12 +258,12 @@ interface Nested<T> {
key: string;
}

function nest<T extends Record<string, any>>(array: readonly T[], prop: string): Nested<T>[] {
function nest<T extends Record<string, any>>(array: readonly T[], property: string): Nested<T>[] {
const nested: Nested<T>[] = [];
const lookup = new Map<string, Nested<T>>();

for (const item of array) {
const key = item[prop];
const key = item[property];
getOrCreate(lookup, key, () => {
const items = {
items: [],
Expand Down
6 changes: 3 additions & 3 deletions cli/run/build.ts
Expand Up @@ -3,7 +3,7 @@ import ms from 'pretty-ms';
import { rollup } from '../../src/node-entry';
import type { MergedRollupOptions } from '../../src/rollup/types';
import { bold, cyan, green } from '../../src/utils/colors';
import { errOnlyInlineSourcemapsForStdout } from '../../src/utils/error';
import { errorOnlyInlineSourcemapsForStdout } from '../../src/utils/error';
import relativeId from '../../src/utils/relativeId';
import { handleError, stderr } from '../logging';
import type { BatchWarnings } from './batchWarnings';
Expand All @@ -22,7 +22,7 @@ export default async function build(
let inputFiles: string | undefined;
if (typeof inputOptions.input === 'string') {
inputFiles = inputOptions.input;
} else if (inputOptions.input instanceof Array) {
} else if (Array.isArray(inputOptions.input)) {
inputFiles = inputOptions.input.join(', ');
} else if (typeof inputOptions.input === 'object' && inputOptions.input !== null) {
inputFiles = Object.values(inputOptions.input).join(', ');
Expand All @@ -34,7 +34,7 @@ export default async function build(
if (useStdout) {
const output = outputOptions[0];
if (output.sourcemap && output.sourcemap !== 'inline') {
handleError(errOnlyInlineSourcemapsForStdout());
handleError(errorOnlyInlineSourcemapsForStdout());
}
const { output: outputs } = await bundle.generate(output);
for (const file of outputs) {
Expand Down
21 changes: 12 additions & 9 deletions cli/run/commandPlugins.ts
Expand Up @@ -45,21 +45,21 @@ async function loadAndRegisterPlugin(
pluginText: string
): Promise<void> {
let plugin: any = null;
let pluginArg: any = undefined;
let pluginArgument: any = undefined;
if (pluginText[0] === '{') {
// -p "{transform(c,i){...}}"
plugin = new Function('return ' + pluginText);
} else {
const match = pluginText.match(/^([@.:/\\\w|^{}-]+)(=(.*))?$/);
const match = pluginText.match(/^([\w./:@\\^{|}-]+)(=(.*))?$/);
if (match) {
// -p plugin
// -p plugin=arg
pluginText = match[1];
pluginArg = new Function('return ' + match[3])();
pluginArgument = new Function('return ' + match[3])();
} else {
throw new Error(`Invalid --plugin argument format: ${JSON.stringify(pluginText)}`);
}
if (!/^\.|^rollup-plugin-|[@/\\]/.test(pluginText)) {
if (!/^\.|^rollup-plugin-|[/@\\]/.test(pluginText)) {
// Try using plugin prefix variations first if applicable.
// Prefix order is significant - left has higher precedence.
for (const prefix of ['@rollup/plugin-', 'rollup-plugin-']) {
Expand All @@ -76,12 +76,12 @@ async function loadAndRegisterPlugin(
if (pluginText[0] == '.') pluginText = resolve(pluginText);
// Windows absolute paths must be specified as file:// protocol URL
// Note that we do not have coverage for Windows-only code paths
else if (pluginText.match(/^[A-Za-z]:\\/)) {
else if (/^[A-Za-z]:\\/.test(pluginText)) {
pluginText = pathToFileURL(resolve(pluginText)).href;
}
plugin = await requireOrImport(pluginText);
} catch (err: any) {
throw new Error(`Cannot load plugin "${pluginText}": ${err.message}.`);
} catch (error: any) {
throw new Error(`Cannot load plugin "${pluginText}": ${error.message}.`);
}
}
}
Expand All @@ -97,12 +97,14 @@ async function loadAndRegisterPlugin(
)}" for Rollup to recognize it.`
);
}
inputOptions.plugins.push(typeof plugin === 'function' ? plugin.call(plugin, pluginArg) : plugin);
inputOptions.plugins.push(
typeof plugin === 'function' ? plugin.call(plugin, pluginArgument) : plugin
);
}

function getCamelizedPluginBaseName(pluginText: string): string {
return (pluginText.match(/(@rollup\/plugin-|rollup-plugin-)(.+)$/)?.[2] || pluginText)
.split(/[\\/]/)
.split(/[/\\]/)
.slice(-1)[0]
.split('.')[0]
.split('-')
Expand All @@ -112,6 +114,7 @@ function getCamelizedPluginBaseName(pluginText: string): string {

async function requireOrImport(pluginPath: string): Promise<any> {
try {
// eslint-disable-next-line unicorn/prefer-module
return require(pluginPath);
} catch {
return import(pluginPath);
Expand Down
22 changes: 12 additions & 10 deletions cli/run/getConfigPath.ts
@@ -1,7 +1,7 @@
import { promises as fs } from 'node:fs';
import { resolve } from 'node:path';
import { cwd } from 'node:process';
import { errMissingExternalConfig } from '../../src/utils/error';
import { errorMissingExternalConfig } from '../../src/utils/error';
import { handleError } from '../logging';

const DEFAULT_CONFIG_BASE = 'rollup.config';
Expand All @@ -11,28 +11,30 @@ export async function getConfigPath(commandConfig: string | true): Promise<strin
return resolve(await findConfigFileNameInCwd());
}
if (commandConfig.slice(0, 5) === 'node:') {
const pkgName = commandConfig.slice(5);
const packageName = commandConfig.slice(5);
try {
return require.resolve(`rollup-config-${pkgName}`, { paths: [cwd()] });
// eslint-disable-next-line unicorn/prefer-module
return require.resolve(`rollup-config-${packageName}`, { paths: [cwd()] });
} catch {
try {
return require.resolve(pkgName, { paths: [cwd()] });
} catch (err: any) {
if (err.code === 'MODULE_NOT_FOUND') {
handleError(errMissingExternalConfig(commandConfig));
// eslint-disable-next-line unicorn/prefer-module
return require.resolve(packageName, { paths: [cwd()] });
} catch (error: any) {
if (error.code === 'MODULE_NOT_FOUND') {
handleError(errorMissingExternalConfig(commandConfig));
}
throw err;
throw error;
}
}
}
return resolve(commandConfig);
}

async function findConfigFileNameInCwd(): Promise<string> {
const filesInWorkingDir = new Set(await fs.readdir(cwd()));
const filesInWorkingDirectory = new Set(await fs.readdir(cwd()));
for (const extension of ['mjs', 'cjs', 'ts']) {
const fileName = `${DEFAULT_CONFIG_BASE}.${extension}`;
if (filesInWorkingDir.has(fileName)) return fileName;
if (filesInWorkingDirectory.has(fileName)) return fileName;
}
return `${DEFAULT_CONFIG_BASE}.js`;
}