diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..fd9c2f2
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,20 @@
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+# indent_size = 8 # should be set in the local editor
+# `max_line_length` will overwrite prettier's default print_width
+max_line_length = 120
+indent_style = tab
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.md]
+trim_trailing_whitespace = false
+indent_style = space
+indent_size = 2
+
+[*.{yml,yaml}]
+indent_style = space
+indent_size = 2
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..243504d
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,15 @@
+---
+version: 2
+updates:
+ - package-ecosystem: 'github-actions'
+ directory: '/'
+ open-pull-requests-limit: 10
+ schedule:
+ interval: 'weekly'
+ day: 'sunday'
+ - package-ecosystem: npm
+ directory: '/'
+ open-pull-requests-limit: 10
+ schedule:
+ interval: 'weekly'
+ day: 'sunday'
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0585cbc
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,23 @@
+# Dependency directories
+node_modules/
+.yarn/
+package-lock.json
+**/*.bun
+
+# Editor folders
+.idea/
+.vscode/
+
+# Logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+lerna-debug.log*
+.pnpm-debug.log*
+
+# Operating system files
+.DS_Store
+
+# Keys
+.env
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 0000000..3a2f87e
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,15 @@
+ISC License
+
+Copyright (c) 2024 SWR Audio lab
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..2eafb15
--- /dev/null
+++ b/README.md
@@ -0,0 +1,126 @@
+
@swrlab/style-guide
+
+> SWR Audio Lab Style Guide
+
+
+
+
+[![license](https://img.shields.io/github/license/swrlab/style-guide?label=license)](https://github.com/swrlab/style-guide/blob/main/LICENSE)
+[![version](https://img.shields.io/npm/v/@swrlab/style-guide)](https://www.npmjs.com/package/swrlab/style-guide)
+
+
+
+|
+SWR Audio Lab's Style Guide 💅
+ |
+
+
+## Introduction
+
+This repository is the home of an based on Vercel's style guide, which includes configs for
+popular linting and styling tools.
+
+The following configs are available, and are designed to be used together.
+
+- [Prettier](#prettier)
+- [ESLint](#eslint)
+- [Biome](#biome)
+
+## Installation
+
+All of our configs are contained in one package, `@swrlab/style-guide`. To install:
+
+```sh
+# If you use Bun
+bun add --dev @swrlab/style-guide
+
+# If you use Yarn
+yarn add --dev @swrlab/style-guide
+
+# If you use npm
+npm i --save-dev @swrlab/style-guide
+
+# If you use pmpm
+pnpm i --save-dev @swrlab/style-guide
+
+```
+
+Some of our ESLint configs require peer dependencies. We'll note those
+alongside the available configs in the [ESLint](#eslint) section.
+
+## Prettier
+
+> Note: Prettier is a peer-dependency of this package, and should be installed
+> at the root of your project.
+>
+> See: https://prettier.io/docs/en/install.html
+
+To use the shared Prettier config, set the following in `package.json`.
+
+```json
+{
+ "prettier": "@swrlab/style-guide/prettier"
+}
+```
+
+## ESLint
+
+> Note: ESLint is a peer-dependency of this package, and should be installed
+> at the root of your project.
+>
+> See: https://eslint.org/docs/user-guide/getting-started#installation-and-usage
+
+Usage:
+
+```js
+// eslint.config.mjs
+import { audiolab } from '@swrlab/style-guide/eslint/presets.js'
+
+export default audiolab(
+ [
+ /* your custom ESLint config */
+ ],
+ {
+ prettier: true,
+ vue: true
+ },
+)
+```
+
+### Presets
+
+You can also import and compose individual presets. However, it is recommended that you use the factory function above.
+
+```js
+// eslint.config.js
+import { presetAll, presetBasic } from '@swrlab/style-guide/eslint/presets.js'
+
+export default presetBasic;
+```
+
+
+## Biome
+
+To use the shared Biome config, set the following in `biome.json`:
+
+```json
+{
+ "extends": ["@swrlab/style-guide/biome"]
+}
+```
+
+## Credits
+
+This config is inspired by the work of [The Vercel Style Guide](https://github.com/vercel/style-guide) and is further
+based on
+
+* https://github.com/antfu/eslint-config
+* https://github.com/sxzz/eslint-config
+
+## Contributing
+
+After cloning, you can run `bun install` (or `npm install`) to install the npm dependencies.
+
+## License
+
+ISC
diff --git a/biome/biome.json b/biome/biome.json
new file mode 100644
index 0000000..814fea5
--- /dev/null
+++ b/biome/biome.json
@@ -0,0 +1,17 @@
+{
+ "$schema": "https://biomejs.dev/schemas/1.8.3/schema.json",
+ "organizeImports": {
+ "enabled": true
+ },
+ "formatter": {
+ "indentStyle": "tab",
+ "formatWithErrors": true
+ },
+ "javascript": {
+ "formatter": {
+ "semicolons": "asNeeded",
+ "trailingComma": "es5",
+ "quoteStyle": "single"
+ }
+ }
+}
diff --git a/bun.lockb b/bun.lockb
new file mode 100755
index 0000000..d0140b6
Binary files /dev/null and b/bun.lockb differ
diff --git a/eslint.config.js b/eslint.config.js
new file mode 100644
index 0000000..b9abcf4
--- /dev/null
+++ b/eslint.config.js
@@ -0,0 +1,19 @@
+import { audiolab } from './eslint/presets.js'
+
+export default audiolab(
+ [
+ {
+ ignores: [],
+ },
+ {
+ rules: {
+ 'import/extensions': ['error', 'ignorePackages'],
+ },
+ },
+ ],
+ {
+ prettier: true,
+ comments: true,
+ vue: true,
+ }
+)
diff --git a/eslint/configs/comments.js b/eslint/configs/comments.js
new file mode 100644
index 0000000..6d6c1a4
--- /dev/null
+++ b/eslint/configs/comments.js
@@ -0,0 +1,31 @@
+import { pluginComments } from '../plugins.js'
+
+/**
+ * @typedef {import("eslint-define-config").FlatESLintConfigItem} FlatESLintConfigItem
+ */
+
+/**
+ * @returns {FlatESLintConfigItem[]}
+ */
+export const comments = [
+ {
+ plugins: {
+ 'eslint-comments': pluginComments,
+ },
+ rules: {
+ ...pluginComments.configs.recommended.rules,
+ /**
+ * Warn if ESlint enable directives are missing after disable directives.
+ *
+ * 🚫 Not fixable - https://mysticatea.github.io/eslint-plugin-eslint-comments/rules/disable-enable-pair.html
+ */
+ 'eslint-comments/disable-enable-pair': ['error', { allowWholeFile: true }],
+ /**
+ * Require comments on ESlint disable directives.
+ *
+ * 🚫 Not fixable - https://mysticatea.github.io/eslint-plugin-eslint-comments/rules/require-description.html
+ */
+ 'eslint-comments/require-description': 'error',
+ },
+ },
+]
diff --git a/eslint/configs/errors.js b/eslint/configs/errors.js
new file mode 100644
index 0000000..c96fd25
--- /dev/null
+++ b/eslint/configs/errors.js
@@ -0,0 +1,44 @@
+/** @type {import("eslint-define-config").FlatESLintConfigItem[]} */
+export const errors = [
+ {
+ rules: {
+ /**
+ * Disallow await inside of loops.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/no-await-in-loop
+ */
+ 'no-await-in-loop': 'error',
+ /**
+ * Allow the use of console.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/no-console
+ */
+ 'no-console': 'error',
+ /**
+ * Disallow expressions where the operation doesn't affect the value.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/no-constant-binary-expression
+ */
+ 'no-constant-binary-expression': 'error',
+ /**
+ * Disallow returning values from Promise executor functions.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/no-promise-executor-return
+ */
+ 'no-promise-executor-return': 'error',
+ /**
+ * Disallow template literal placeholder syntax in regular strings, as
+ * these are likely errors.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/no-template-curly-in-string
+ */
+ 'no-template-curly-in-string': 'error',
+ /**
+ * Disallow loops with a body that allows only one iteration.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/no-unreachable-loop
+ */
+ 'no-unreachable-loop': 'error',
+ },
+ },
+]
diff --git a/eslint/configs/ignores.js b/eslint/configs/ignores.js
new file mode 100644
index 0000000..9857568
--- /dev/null
+++ b/eslint/configs/ignores.js
@@ -0,0 +1,6 @@
+import { GLOB_EXCLUDE } from '../constants.js'
+
+/**
+ * @type {import("eslint-define-config").FlatESLintConfigItem[]}
+ */
+export const ignores = [{ ignores: GLOB_EXCLUDE }]
diff --git a/eslint/configs/imports.js b/eslint/configs/imports.js
new file mode 100644
index 0000000..9aca4ad
--- /dev/null
+++ b/eslint/configs/imports.js
@@ -0,0 +1,102 @@
+import { GLOB_SRC_EXT } from '../constants.js'
+import { pluginImport } from '../plugins.js'
+
+export const imports = [
+ {
+ plugins: {
+ import: pluginImport,
+ },
+ rules: {
+ // TODO: add recommended rules (if ready for Flat Config)
+
+ /**
+ * Disallow non-import statements appearing before import statements.
+ *
+ * 🚫 Not fixable - https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/first.md
+ */
+ 'import/first': 'error',
+ /**
+ * Require a newline after the last import/require.
+ *
+ * 🔧 Fixable - https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/newline-after-import.md
+ */
+ 'import/newline-after-import': 'warn',
+ /**
+ * Disallow import of modules using absolute paths.
+ *
+ * 🚫 Not fixable - https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-absolute-path.md
+ */
+ 'import/no-absolute-path': 'error',
+ /**
+ * Disallow cyclical dependencies between modules.
+ *
+ * 🚫 Not fixable - https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-cycle.md
+ */
+ 'import/no-cycle': 'error',
+ /**
+ * Disallow default exports.
+ *
+ * 🚫 Not fixable - https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-default-export.md
+ */
+ 'import/no-default-export': 'error',
+ /**
+ * Disallow the use of extraneous packages.
+ *
+ * 🚫 Not fixable - https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-extraneous-dependencies.md
+ */
+ 'import/no-extraneous-dependencies': ['error', { includeTypes: true }],
+ /**
+ * Disallow mutable exports.
+ *
+ * 🚫 Not fixable - https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-mutable-exports.md
+ */
+ 'import/no-mutable-exports': 'error',
+ /**
+ * Disallow importing packages through relative paths.
+ *
+ * 🚫 Not fixable - https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-relative-packages.md
+ */
+ 'import/no-relative-packages': 'warn',
+ /**
+ * Disallow a module from importing itself.
+ *
+ * 🚫 Not fixable - https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-self-import.md
+ */
+ 'import/no-self-import': 'error',
+ /**
+ * Ensures that there are no useless path segments.
+ *
+ * 🚫 Not fixable - https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-useless-path-segments.md
+ */
+ 'import/no-useless-path-segments': ['error'],
+ /**
+ * Enforce a module import order convention.
+ *
+ * 🔧 Fixable - https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/order.md
+ */
+ 'import/order': [
+ 'warn',
+ {
+ groups: [
+ 'builtin', // Node.js built-in modules
+ 'external', // Packages
+ 'internal', // Aliased modules
+ 'parent', // Relative parent
+ 'sibling', // Relative sibling
+ 'index', // Relative index
+ ],
+ 'newlines-between': 'never',
+ },
+ ],
+ },
+ },
+ {
+ files: [`**/*config*.${GLOB_SRC_EXT}`, '**/index.js', '**/*.d.ts', '**/.prettierrc*'],
+ plugins: {
+ import: pluginImport,
+ },
+ rules: {
+ 'import/no-default-export': 'off',
+ },
+ },
+]
diff --git a/eslint/configs/index.js b/eslint/configs/index.js
new file mode 100644
index 0000000..76640bd
--- /dev/null
+++ b/eslint/configs/index.js
@@ -0,0 +1,16 @@
+export * from './comments.js'
+export * from './ignores.js'
+export * from './imports.js'
+export * from './errors.js'
+export * from './javascript.js'
+export * from './node.js'
+export * from './prettier.js'
+export * from './json.js'
+export * from './perfectionist.js'
+export * from './sort.js'
+export * from './unicorn.js'
+export * from './vue.js'
+export * from './security.js'
+export * from './sonarjs.js'
+// TODO: add yaml config
+// export * from './yml.js'
diff --git a/eslint/configs/javascript.js b/eslint/configs/javascript.js
new file mode 100644
index 0000000..90d9dc2
--- /dev/null
+++ b/eslint/configs/javascript.js
@@ -0,0 +1,444 @@
+import globals from 'globals'
+import { pluginJs } from '../plugins.js'
+import { ECMA_VERSION } from '../constants.js'
+/**
+ * @typedef {import('eslint-define-config').FlatESLintConfigItem} FlatESLintConfigItem
+ */
+
+const USE_JSX = true
+
+/**
+ * @returns {FlatESLintConfigItem[]}
+ */
+export const javascript = [
+ pluginJs.configs.recommended,
+ {
+ languageOptions: {
+ ecmaVersion: ECMA_VERSION,
+ globals: {
+ ...globals.browser,
+ ...globals.es2021,
+ ...globals.node,
+ document: 'readonly',
+ navigator: 'readonly',
+ window: 'readonly',
+ },
+ parserOptions: {
+ ecmaFeatures: {
+ jsx: USE_JSX,
+ },
+ ecmaVersion: ECMA_VERSION,
+ sourceType: 'module',
+ },
+ sourceType: 'module',
+ },
+ linterOptions: {
+ // noInlineConfig: true, // commented out, since we allow inline configs for now
+ reportUnusedDisableDirectives: true,
+ },
+ rules: {
+ // Best practises
+ /**
+ * Require return statements in array methods callbacks.
+ *
+ * 🚫 Not fixable -https://eslint.org/docs/rules/array-callback-return
+ */
+ 'array-callback-return': ['error', { allowImplicit: true }],
+ /**
+ * Treat `var` statements as if they were block scoped.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/block-scoped-var
+ */
+ 'block-scoped-var': 'error',
+ /**
+ * Require curly braces for multiline blocks or nested and be consistent.
+ * Disabled for now
+ *
+ * 🔧 Fixable - https://eslint.org/docs/rules/curly
+ */
+ // curly: ['warn', 'multi-or-nest', 'consistent'],
+ /**
+ * Require default clauses in switch statements to be last (if used).
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/default-case-last
+ */
+ 'default-case-last': 'error',
+ /**
+ * Require triple equals (`===` and `!==`).
+ *
+ * 🔧 Fixable - https://eslint.org/docs/rules/eqeqeq
+ */
+ eqeqeq: 'error',
+ /**
+ * Require grouped accessor pairs in object literals and classes.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/grouped-accessor-pairs
+ */
+ 'grouped-accessor-pairs': 'error',
+ /**
+ * Disallow use of `alert()`.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/no-alert
+ */
+ 'no-alert': 'error',
+ /**
+ * Disallow use of `caller`/`callee`.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/no-caller
+ */
+ 'no-caller': 'error',
+ /**
+ * Disallow returning value in constructor.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/no-constructor-return
+ */
+ 'no-constructor-return': 'error',
+ /**
+ * Disallow using an `else` if the `if` block contains a return.
+ *
+ * 🔧 Fixable - https://eslint.org/docs/rules/no-else-return
+ */
+ 'no-else-return': 'warn',
+ /**
+ * Disallow `eval()`.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/no-eval
+ */
+ 'no-eval': 'error',
+ /**
+ * Disallow extending native objects.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/no-extend-native
+ */
+ 'no-extend-native': 'error',
+ /**
+ * Disallow unnecessary function binding.
+ *
+ * 🔧 Fixable - https://eslint.org/docs/rules/no-extra-bind
+ */
+ 'no-extra-bind': 'error',
+ /**
+ * Disallow unnecessary labels.
+ *
+ * 🔧 Fixable - https://eslint.org/docs/rules/no-extra-label
+ */
+ 'no-extra-label': 'error',
+ /**
+ * Disallow floating decimals.
+ *
+ * 🔧 Fixable - https://eslint.org/docs/rules/no-floating-decimal
+ */
+ 'no-floating-decimal': 'error',
+ /**
+ * Make people convert types explicitly e.g. `Boolean(foo)` instead of `!!foo`.
+ * For now not enabled.
+ *
+ * 🔧 Partially Fixable - https://eslint.org/docs/rules/no-implicit-coercion
+ */
+ 'no-implicit-coercion': 'off', // { disallowTemplateShorthand: true, allow: ['!!'] }
+ /**
+ * Disallow use of `eval()`-like methods.
+ *
+ * https://eslint.org/docs/rules/no-implied-eval
+ */
+ 'no-implied-eval': 'error',
+ /**
+ * Disallow usage of `__iterator__` property.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/no-iterator
+ */
+ 'no-iterator': 'error',
+ /**
+ * Disallow use of labels for anything other than loops and switches.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/no-labels
+ */
+ 'no-labels': ['error'],
+ /**
+ * Disallow unnecessary nested blocks.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/no-lone-blocks
+ */
+ 'no-lone-blocks': 'error',
+ /**
+ * Disallow `new` for side effects.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/no-new
+ */
+ 'no-new': 'error',
+ /**
+ * Disallow function constructors.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/no-new-func
+ */
+ 'no-new-func': 'error',
+ /**
+ * Disallow primitive wrapper instances, such as `new String('foo')`.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/no-new-wrappers
+ */
+ 'no-new-wrappers': 'error',
+ /**
+ * Disallow use of octal escape sequences in string literals.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/no-octal-escape
+ */
+ 'no-octal-escape': 'error',
+ /**
+ * Disallow reassignment of function parameters.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/no-param-reassign
+ */
+ 'no-param-reassign': 'error',
+ /**
+ * Disallow usage of the deprecated `__proto__` property.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/no-proto
+ */
+ 'no-proto': 'error',
+ /**
+ * Disallow assignment in `return` statement.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/no-return-assign
+ */
+ 'no-return-assign': 'error',
+ /**
+ * Disallows unnecessary `return await`.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/no-return-await
+ */
+ 'no-return-await': 'error',
+ /**
+ * Disallow use of `javascript:` urls.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/no-script-url
+ */
+ 'no-script-url': 'error',
+ /**
+ * Disallow comparisons where both sides are exactly the same.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/no-self-compare
+ */
+ 'no-self-compare': 'error',
+ /**
+ * Disallow use of comma operator.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/no-sequences
+ */
+ 'no-sequences': 'error',
+ /**
+ * Disallow unnecessary `.call()` and `.apply()`.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/no-useless-call
+ */
+ 'no-useless-call': 'error',
+ /**
+ * Disallow unnecessary concatenation of strings.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/no-useless-concat
+ */
+ 'no-useless-concat': 'error',
+ /**
+ * Disallow redundant return statements.
+ *
+ * 🔧 Fixable - https://eslint.org/docs/rules/no-useless-return
+ */
+ 'no-useless-return': 'warn',
+ /**
+ * Require using named capture groups in regular expressions.
+ * For now off.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/prefer-named-capture-group
+ */
+ 'prefer-named-capture-group': 'off',
+ /**
+ * Require using Error objects as Promise rejection reasons.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/prefer-promise-reject-errors
+ */
+ 'prefer-promise-reject-errors': ['error', { allowEmptyReject: true }],
+ /**
+ * Disallow use of the RegExp constructor in favor of regular expression
+ * literals.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/prefer-regex-literals
+ */
+ 'prefer-regex-literals': 'error',
+ /**
+ * Disallow "Yoda conditions", ensuring the comparison.
+ *
+ * 🔧 Fixable - https://eslint.org/docs/rules/yoda
+ */
+ yoda: 'warn',
+
+ // ES6
+ /**
+ * Disallow useless computed property keys.
+ *
+ * 🔧 Fixable - https://eslint.org/docs/rules/no-useless-computed-key
+ */
+ 'no-useless-computed-key': 'warn',
+ /**
+ * Disallow renaming import, export, and destructured assignments to the
+ * same name.
+ *
+ * 🔧 Fixable - https://eslint.org/docs/rules/no-useless-rename
+ */
+ 'no-useless-rename': 'warn',
+ /**
+ * Require `let` or `const` instead of `var`.
+ *
+ * 🔧 Fixable - https://eslint.org/docs/rules/no-var
+ */
+ 'no-var': 'error',
+ /**
+ * Require object literal shorthand syntax.
+ *
+ * 🔧 Fixable - https://eslint.org/docs/rules/object-shorthand
+ */
+ 'object-shorthand': 'warn',
+ /**
+ * Require default to `const` instead of `let`.
+ *
+ * 🔧 Fixable - https://eslint.org/docs/rules/prefer-const
+ */
+ 'prefer-const': 'warn',
+ /**
+ * Disallow parseInt() in favor of binary, octal, and hexadecimal literals.
+ *
+ * 🔧 Fixable - https://eslint.org/docs/rules/prefer-numeric-literals
+ */
+ 'prefer-numeric-literals': 'error',
+ /**
+ * Require using rest parameters instead of `arguments`.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/prefer-rest-params
+ */
+ 'prefer-rest-params': 'error',
+ /**
+ * Require using spread syntax instead of `.apply()`.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/prefer-spread
+ */
+ 'prefer-spread': 'error',
+ /**
+ * Require using template literals instead of string concatenation.
+ *
+ * 🔧 Fixable - https://eslint.org/docs/rules/prefer-template
+ */
+ 'prefer-template': 'warn',
+ /**
+ * Require a `Symbol` description.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/symbol-description
+ */
+ 'symbol-description': 'error',
+
+ // Stylistic
+ /**
+ * Require camel case names.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/camelcase
+ */
+ camelcase: ['error', { allow: ['^UNSAFE_'], ignoreDestructuring: false, properties: 'never' }],
+ /**
+ * Require function expressions to have a name.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/func-names
+ */
+ 'func-names': ['error', 'as-needed'],
+ /**
+ * Require a capital letter for constructors.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/new-cap
+ */
+ 'new-cap': ['error', { capIsNew: false }],
+ /**
+ * Disallow the omission of parentheses when invoking a constructor with
+ * no arguments.
+ *
+ * 🔧 Fixable - https://eslint.org/docs/rules/new-parens
+ */
+ 'new-parens': 'warn',
+ /**
+ * Disallow use of the Array constructor.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/no-array-constructor
+ */
+ 'no-array-constructor': 'error',
+ /**
+ * Disallow use of bitwise operators.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/no-bitwise
+ */
+ 'no-bitwise': 'error',
+ /**
+ * Disallow if as the only statement in an else block.
+ *
+ * 🔧 Fixable - https://eslint.org/docs/rules/no-lonely-if
+ */
+ 'no-lonely-if': 'warn',
+ /**
+ * Disallow use of chained assignment expressions.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/no-multi-assign
+ */
+ 'no-multi-assign': ['error'],
+ /**
+ * Disallow nested ternary expressions.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/no-nested-ternary
+ */
+ 'no-nested-ternary': 'error',
+ /**
+ * Disallow ternary operators when simpler alternatives exist.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/no-unneeded-ternary
+ */
+ 'no-unneeded-ternary': 'error',
+ /**
+ * Require use of an object spread over Object.assign.
+ *
+ * 🔧 Fixable - https://eslint.org/docs/rules/prefer-object-spread
+ */
+ 'prefer-object-spread': 'warn',
+
+ // Variables
+ /**
+ * Require one variable declaration per scope.
+ * Require consecutive let declarations to be a single declaration.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/one-var
+ */
+ 'one-var': ['error', { let: 'consecutive' }],
+ /**
+ * Disallow labels that share a name with a variable.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/no-label-var
+ */
+ 'no-label-var': 'error',
+ /**
+ * Disallow initializing variables to `undefined`.
+ *
+ * 🔧 Fixable - https://eslint.org/docs/rules/no-undef-init
+ */
+ 'no-undef-init': 'warn',
+ /**
+ * Disallow unused variables.
+ *
+ * 🚫 Not fixable - https://eslint.org/docs/rules/no-unused-vars
+ */ 'no-unused-vars': [
+ 'error',
+ {
+ args: 'after-used',
+ argsIgnorePattern: '^_',
+ ignoreRestSiblings: false,
+ vars: 'all',
+ varsIgnorePattern: '^_',
+ },
+ ],
+
+ // Audiolab preferences
+ },
+ },
+]
diff --git a/eslint/configs/json.js b/eslint/configs/json.js
new file mode 100644
index 0000000..182c1b8
--- /dev/null
+++ b/eslint/configs/json.js
@@ -0,0 +1,4 @@
+import { pluginJsonc } from '../plugins.js'
+
+/** @type {import("eslint-define-config").FlatESLintConfigItem[]} */
+export const json = [...pluginJsonc.configs['flat/recommended-with-json']]
diff --git a/eslint/configs/node.js b/eslint/configs/node.js
new file mode 100644
index 0000000..45a774e
--- /dev/null
+++ b/eslint/configs/node.js
@@ -0,0 +1,18 @@
+import { pluginNode } from '../plugins.js'
+
+export const node = [
+ pluginNode.configs['flat/recommended-script'],
+ {
+ rules: {
+ 'n/handle-callback-err': ['error', '^(err|error)$'],
+ 'n/no-deprecated-api': 'error',
+ 'n/no-exports-assign': 'error',
+ 'n/no-new-require': 'error',
+ 'n/no-path-concat': 'error',
+ 'n/no-unsupported-features/es-builtins': 'error',
+ 'n/prefer-global/buffer': ['error', 'never'],
+ 'n/prefer-global/process': ['error', 'never'],
+ 'n/process-exit-as-throw': 'error',
+ },
+ },
+]
diff --git a/eslint/configs/perfectionist.js b/eslint/configs/perfectionist.js
new file mode 100644
index 0000000..f4263b2
--- /dev/null
+++ b/eslint/configs/perfectionist.js
@@ -0,0 +1,10 @@
+import { pluginPerfectionist } from '../plugins.js'
+
+/** @type {import("eslint-define-config").FlatESLintConfigItem[]} */
+export const perfectionist = [
+ {
+ plugins: {
+ perfectionist: pluginPerfectionist,
+ },
+ },
+]
diff --git a/eslint/configs/prettier.js b/eslint/configs/prettier.js
new file mode 100644
index 0000000..31450c5
--- /dev/null
+++ b/eslint/configs/prettier.js
@@ -0,0 +1,27 @@
+import { configPrettier, pluginPrettier } from '../plugins.js'
+
+const prettierConflictRules = { ...configPrettier.rules }
+delete prettierConflictRules['vue/html-self-closing']
+
+/** @type {import('eslint-define-config').FlatESLintConfigItem[]} */
+export const prettier = [
+ // Any other config imports go at the top
+ // pluginPrettierRecommended,
+ {
+ // ignores: ['package.json'],
+ plugins: {
+ prettier: pluginPrettier,
+ },
+ rules: {
+ ...prettierConflictRules,
+ ...pluginPrettier.configs.recommended.rules,
+ 'prettier/prettier': [
+ 'warn',
+ {
+ // printWidth: 120,
+ useTabs: true,
+ },
+ ],
+ },
+ },
+]
diff --git a/eslint/configs/security.js b/eslint/configs/security.js
new file mode 100644
index 0000000..fb94387
--- /dev/null
+++ b/eslint/configs/security.js
@@ -0,0 +1,6 @@
+import { pluginSecurity } from '../plugins.js'
+
+/** @type {import("eslint-define-config").FlatESLintConfigItem[]} */
+// TODO: waiting for https://github.com/eslint-community/eslint-plugin-security/pull/145
+// to be merged
+export const security = [pluginSecurity.configs.recommended]
diff --git a/eslint/configs/sonarjs.js b/eslint/configs/sonarjs.js
new file mode 100644
index 0000000..d6ffa17
--- /dev/null
+++ b/eslint/configs/sonarjs.js
@@ -0,0 +1,15 @@
+import { pluginSonarJS } from '../plugins.js'
+
+/** @type {import("eslint-define-config").FlatESLintConfigItem[]} */
+export const sonarjs = [
+ {
+ plugins: {
+ sonarjs: pluginSonarJS,
+ },
+ rules: {
+ // does not yet work with ESlint Flat Config
+ // ...pluginSonarJS.configs.recommended.rules,
+ 'sonarjs/cognitive-complexity': ['error', 40],
+ },
+ },
+]
diff --git a/eslint/configs/sort.js b/eslint/configs/sort.js
new file mode 100644
index 0000000..445cb6e
--- /dev/null
+++ b/eslint/configs/sort.js
@@ -0,0 +1,76 @@
+export const sortPackageJson = [
+ {
+ files: ['**/package.json'],
+ rules: {
+ 'jsonc/sort-array-values': [
+ 'error',
+ {
+ order: { type: 'asc' },
+ pathPattern: '^files$',
+ },
+ ],
+ 'jsonc/sort-keys': [
+ 'error',
+ {
+ order: [
+ 'name',
+ 'version',
+ 'private',
+ 'packageManager',
+ 'description',
+ 'type',
+ 'keywords',
+ 'license',
+ 'homepage',
+ 'bugs',
+ 'repository',
+ 'author',
+ 'contributors',
+ 'funding',
+ 'files',
+ 'main',
+ 'module',
+ 'types',
+ 'exports',
+ 'typesVersions',
+ 'sideEffects',
+ 'unpkg',
+ 'jsdelivr',
+ 'browser',
+ 'bin',
+ 'man',
+ 'directories',
+ 'publishConfig',
+ 'scripts',
+ 'peerDependencies',
+ 'peerDependenciesMeta',
+ 'optionalDependencies',
+ 'dependencies',
+ 'devDependencies',
+ 'engines',
+ 'config',
+ 'overrides',
+ 'pnpm',
+ 'husky',
+ 'lint-staged',
+ 'eslintConfig',
+ 'prettier',
+ ],
+ pathPattern: '^$',
+ },
+ {
+ order: { type: 'asc' },
+ pathPattern: '^(?:dev|peer|optional|bundled)?[Dd]ependencies(Meta)?$',
+ },
+ {
+ order: ['types', 'require', 'import', 'default'],
+ pathPattern: '^exports.*$',
+ },
+ {
+ order: { type: 'asc' },
+ pathPattern: '^(?:resolutions|overrides|pnpm.overrides)$',
+ },
+ ],
+ },
+ },
+]
diff --git a/eslint/configs/unicorn.js b/eslint/configs/unicorn.js
new file mode 100644
index 0000000..3dd21aa
--- /dev/null
+++ b/eslint/configs/unicorn.js
@@ -0,0 +1,29 @@
+import { pluginUnicorn } from '../plugins.js'
+
+export const unicorn = [
+ {
+ plugins: {
+ unicorn: pluginUnicorn,
+ },
+ rules: {
+ /**
+ * Require consistent filename case for all linted files.
+ * Disabled for now.
+ *
+ * 🚫 Not fixable - https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/filename-case.md
+ */
+ // 'unicorn/filename-case': [
+ // 'error',
+ // {
+ // case: 'kebabCase',
+ // },
+ // ],
+ /**
+ * Require using the `node:` protocol when importing Node.js built-in modules.
+ *
+ * 🔧 Fixable - https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-node-protocol.md
+ */
+ 'unicorn/prefer-node-protocol': 'warn',
+ },
+ },
+]
diff --git a/eslint/configs/vue.js b/eslint/configs/vue.js
new file mode 100644
index 0000000..ba34aeb
--- /dev/null
+++ b/eslint/configs/vue.js
@@ -0,0 +1,4 @@
+import { pluginVue } from '../plugins.js'
+
+/** @type {import("eslint-define-config").FlatESLintConfigItem[]} */
+export const vue = [...pluginVue.configs['flat/essential']]
diff --git a/eslint/constants.js b/eslint/constants.js
new file mode 100644
index 0000000..bf26281
--- /dev/null
+++ b/eslint/constants.js
@@ -0,0 +1,25 @@
+export const ECMA_VERSION = 2022
+export const GLOB_SRC_EXT = '?([cm])[jt]s?(x)'
+export const GLOB_JS = '**/*.?([cm])js'
+export const JAVASCRIPT_FILES = ['*.js?(x)', '*.mjs']
+export const TYPESCRIPT_FILES = ['*.ts?(x)']
+export const GLOB_EXCLUDE = [
+ '**/node_modules',
+ '**/dist',
+ '**/package-lock.json',
+ '**/yarn.lock',
+ '**/pnpm-lock.yaml',
+ '**/bun.lockb',
+
+ '**/coverage',
+ '**/.idea',
+ '**/.output',
+ '**/.vite-inspect',
+ '**/.nitro',
+
+ '**/*.min.*',
+
+ 'CHANGELOG*.md',
+ '**/CHANGELOG*.md',
+]
+export const GLOB_JSON = '**/*.json'
diff --git a/eslint/index.js b/eslint/index.js
new file mode 100644
index 0000000..5975393
--- /dev/null
+++ b/eslint/index.js
@@ -0,0 +1,5 @@
+export * from './configs/index.js'
+export * from './presets.js'
+// TODO: handle constants
+export * from './constants.js'
+export * from './plugins.js'
diff --git a/eslint/plugins.js b/eslint/plugins.js
new file mode 100644
index 0000000..9acd356
--- /dev/null
+++ b/eslint/plugins.js
@@ -0,0 +1,29 @@
+export { default as pluginJs } from '@eslint/js'
+export { default as pluginComments } from 'eslint-plugin-eslint-comments'
+// TODO: Are there benefits using eslint-plugin-import?
+export * as pluginImport from 'eslint-plugin-import-x'
+export { default as pluginNode } from 'eslint-plugin-n'
+export { default as pluginUnicorn } from 'eslint-plugin-unicorn'
+export { default as pluginJsonc } from 'eslint-plugin-jsonc'
+
+export * as pluginSonarJS from 'eslint-plugin-sonarjs'
+
+// required ts-eslint (parser)
+// export { default as pluginUnusedImports } from 'eslint-plugin-unused-imports'
+// export * as pluginUnusedImports from 'eslint-plugin-unused-imports'
+
+export { default as pluginPerfectionist } from 'eslint-plugin-perfectionist'
+
+export { default as pluginVue } from 'eslint-plugin-vue'
+
+// import tseslint from 'typescript-eslint'
+// export { tseslint }
+
+// TODO: remove unused recommended config
+// export { default as pluginPrettierRecommended } from 'eslint-plugin-prettier/recommended'
+export { default as pluginPrettier } from 'eslint-plugin-prettier'
+export * as configPrettier from 'eslint-config-prettier'
+
+export { default as pluginSecurity } from 'eslint-plugin-security'
+
+// export * as parserVue from 'vue-eslint-parser'
diff --git a/eslint/presets.js b/eslint/presets.js
new file mode 100644
index 0000000..176966c
--- /dev/null
+++ b/eslint/presets.js
@@ -0,0 +1,72 @@
+import {
+ ignores,
+ comments,
+ errors,
+ imports,
+ javascript,
+ node,
+ prettier,
+ json,
+ perfectionist,
+ sortPackageJson,
+ unicorn,
+ vue,
+ sonarjs,
+} from './configs/index.js'
+
+/**
+ * @typedef {import('eslint-define-config').FlatESLintConfigItem} FlatESLintConfigItem
+ */
+
+/** Ignore common files and include javascript support */
+export const presetJavaScript = [
+ ...ignores,
+ ...javascript,
+ ...comments,
+ ...imports,
+ ...errors,
+ ...unicorn,
+ ...node,
+ ...sonarjs,
+]
+/** Includes `presetJavaScript` and typescript support */
+export const presetBasic = [...presetJavaScript, ...json, ...perfectionist, ...sortPackageJson]
+export const presetAll = [...presetBasic, ...vue, ...prettier]
+export { presetBasic as basic, presetAll as all }
+
+const migrationOverwrites = [
+ {
+ rules: {
+ 'eslint-comments/require-description': 'off',
+ 'unicorn/filename-case': 'off',
+ 'unicorn/prefer-node-protocol': 'off',
+ 'import/order': 'off',
+ },
+ },
+]
+
+/**
+ *
+ * @param {FlatESLintConfigItem | FlatESLintConfigItem[]} config
+ * @param {{prettier: boolean, vue: boolean, cjs: boolean, migrate: boolean}} features
+ * @returns {FlatESLintConfigItem[]}
+ */
+export function audiolab(
+ config = [],
+ { prettier: enablePrettier = true, vue: enableVue = true, migrate = false } = {}
+) {
+ const configs = [...presetBasic]
+ if (enableVue) {
+ configs.push(...vue)
+ }
+ if (enablePrettier) {
+ configs.push(...prettier)
+ }
+ if (Object.keys(config).length > 0) {
+ configs.push(...(Array.isArray(config) ? config : [config]))
+ }
+ if (migrate) {
+ configs.push(...migrationOverwrites)
+ }
+ return configs
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..3c7a5c1
--- /dev/null
+++ b/package.json
@@ -0,0 +1,82 @@
+{
+ "name": "@swrlab/style-guide",
+ "version": "1.0.0",
+ "packageManager": "yarn@4.1.1",
+ "description": "SWR Audio Lab's engineering style guide",
+ "type": "module",
+ "license": "ISC",
+ "homepage": "https://github.com/swrlab/style-guide#readme",
+ "bugs": {
+ "url": "https://github.com/swrlab/style-guide/issues"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/swrlab/style-guide.git"
+ },
+ "files": [
+ "biome",
+ "eslint",
+ "prettier"
+ ],
+ "main": "index.js",
+ "exports": {
+ "./biome": "./biome/biome.json",
+ "./eslint/*": "./eslint/*.js",
+ "./prettier": "./prettier/index.js"
+ },
+ "publishConfig": {
+ "access": "public"
+ },
+ "scripts": {
+ "lint": "eslint --max-warnings=0 .",
+ "lint:fix": "eslint --max-warnings=0 --fix .",
+ "prettier-check": "prettier --check .",
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "peerDependencies": {
+ "eslint": ">=8.48.0",
+ "prettier": ">=3.0.0"
+ },
+ "peerDependenciesMeta": {
+ "eslint": {
+ "optional": true
+ },
+ "prettier": {
+ "optional": true
+ }
+ },
+ "dependencies": {
+ "@babel/core": "^7.24.7",
+ "@babel/eslint-parser": "^7.24.7",
+ "@eslint/js": "^9.6.0",
+ "@stylistic/eslint-plugin-js": "^1.7.0",
+ "eslint-config-prettier": "^9.1.0",
+ "eslint-import-resolver-alias": "^1.1.2",
+ "eslint-plugin-eslint-comments": "^3.2.0",
+ "eslint-plugin-import-x": "^0.5.2",
+ "eslint-plugin-jsonc": "^2.16.0",
+ "eslint-plugin-n": "^17.9.0",
+ "eslint-plugin-perfectionist": "^2.11.0",
+ "eslint-plugin-prettier": "^5.1.3",
+ "eslint-plugin-security": "^2.1.1",
+ "eslint-plugin-sonarjs": "^0.25.1",
+ "eslint-plugin-testing-library": "^6.2.2",
+ "eslint-plugin-unicorn": "^52.0.0",
+ "eslint-plugin-unused-imports": "^3.1.0",
+ "eslint-plugin-vue": "^9.26.0",
+ "globals": "^15.7.0"
+ },
+ "devDependencies": {
+ "eslint": "^9.6.0",
+ "eslint-define-config": "^2.1.0",
+ "lint-staged": "^15.2.7",
+ "prettier": "^3.3.2"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "lint-staged": {
+ "*": "prettier -w --ignore-unknown"
+ },
+ "prettier": "./prettier/index.js"
+}
diff --git a/prettier/index.js b/prettier/index.js
new file mode 100644
index 0000000..b335640
--- /dev/null
+++ b/prettier/index.js
@@ -0,0 +1,40 @@
+/**
+ * Some of Prettier's defaults can be overridden by an EditorConfig file.
+ * We define those here to ensure that doesn't happen.
+ *
+ * See: https://github.com/prettier/prettier/blob/main/docs/configuration.md#editorconfig
+ */
+const overridableDefaults = {
+ endOfLine: 'lf',
+ // tabWidth is neither set in .editorconfig nor here, so every developer
+ // can set their own preference in their editor locally.
+ // tabWidth: 8,
+ // default is 80
+ // same goes with `printWidth`. We inherit this value from the local editorconfig.
+ // printWidth: 120,
+ useTabs: true,
+ semi: false,
+ trailingComma: 'es5',
+}
+
+/**
+ * @type {import('prettier').Config}
+ * @see https://prettier.io/docs/en/options.html
+ */
+export const config = {
+ ...overridableDefaults,
+ singleQuote: true,
+ // Conflicts with sortPackagejson (from sort.js)
+ // plugins: ['prettier-plugin-packagejson'],
+ overrides: [
+ {
+ files: ['*.yml', '*.yaml', '*.md'],
+ options: {
+ useTabs: false,
+ tabWidth: 2,
+ },
+ },
+ ],
+}
+
+export default config