diff --git a/.husky/pre-commit b/.husky/pre-commit old mode 100644 new mode 100755 index 0cf3a8f..250844c --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1 +1 @@ -npx lint-staged --config lint-staged.config.js \ No newline at end of file +npx blocks internal-lint \ No newline at end of file diff --git a/bin/cli.js b/bin/cli.js new file mode 100755 index 0000000..7969b30 --- /dev/null +++ b/bin/cli.js @@ -0,0 +1,106 @@ +#!/usr/bin/env node + +/*global console*/ + +import { execSync } from 'child_process'; +import path from 'path'; +import fs from 'fs'; +import process from 'process'; +import { fileURLToPath } from 'url'; +import { createRequire } from 'module'; + +const require = createRequire(import.meta.url); +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +// --- Path Resolution --- +const userAppRoot = process.cwd(); +const engineRoot = path.resolve(__dirname, '..'); +const internalModulesPath = path.resolve(engineRoot, 'node_modules'); +const internalBinPath = path.resolve(internalModulesPath, '.bin'); + +// Safely find binary paths for the internal tools +const getBinPath = (pkgName, binSubPath) => { + try { + const pkgRoot = path.dirname(require.resolve(`${pkgName}/package.json`)); + return path.join(pkgRoot, binSubPath); + } catch { + return path.resolve(internalBinPath, pkgName); + } +}; + +const huskyBin = getBinPath('husky', 'bin.js'); +const lintStagedBin = getBinPath('lint-staged', 'bin/lint-staged.js'); + +const command = process.argv[2]; + +// --------------------------------------------------------- +// COMMAND: setup-git +// --------------------------------------------------------- +if (command === 'setup-git') { + console.log('🐶 Setting up @build-in-blocks git hooks...'); + try { + execSync(`node "${huskyBin}"`, { stdio: 'inherit' }); + const preCommitPath = path.join(userAppRoot, '.husky/pre-commit'); + + // Use 'npx blocks' to ensure portability in the User App + const hookContent = 'npx blocks internal-lint'; + + fs.writeFileSync(preCommitPath, hookContent, { mode: 0o755 }); + + console.log('✅ Git hooks integrated successfully.'); + } catch { + console.error('❌ Git hook setup failed.'); + } +} + +// --------------------------------------------------------- +// COMMAND: internal-lint +// --------------------------------------------------------- +if (command === 'internal-lint') { + const configPath = path.resolve(engineRoot, 'configs/lint-staged.config.mjs'); + try { + // Detect the correct PATH key (Windows compatibility) + const pathKey = + process.platform === 'win32' + ? Object.keys(process.env).find((k) => k.toUpperCase() === 'PATH') || + 'PATH' + : 'PATH'; + + const env = { + ...process.env, + [pathKey]: `${internalBinPath}${path.delimiter}${process.env[pathKey]}`, + NODE_PATH: internalModulesPath, + NODE_OPTIONS: '--no-warnings', + }; + + execSync(`node "${lintStagedBin}" --config "${configPath}"`, { + stdio: 'inherit', + cwd: userAppRoot, + env, + }); + } catch { + process.exit(1); + } +} + +// if (command === 'internal-lint') { +// const lintStagedConfig = path.resolve(engineRoot, 'configs/lint-staged.config.mjs'); + +// try { +// const env = { +// ...process.env, +// PATH: `${internalBinPath}${path.delimiter}${process.env.PATH}`, +// // We tell Node to look in the engine's node_modules +// // when the User App's config tries to resolve its imports. +// NODE_PATH: internalModulesPath, +// }; + +// execSync(`node ${lintStagedBin} --config ${lintStagedConfig}`, { +// stdio: 'inherit', +// env +// }); +// } catch (e) { +// process.exit(1); +// } +// } diff --git a/configs/eslint.helper.mjs b/configs/eslint.helper.mjs new file mode 100644 index 0000000..2d4555c --- /dev/null +++ b/configs/eslint.helper.mjs @@ -0,0 +1,19 @@ + +import { createRequire } from 'module'; +import blocksDevSetupBaseConfig from '../eslint.config.mjs'; + +const require = createRequire(import.meta.url); + +// Dynamic Discovery: Find ESLint wherever the package manager put it +const eslintConfigPath = require.resolve('eslint/config'); +const { defineConfig } = require(eslintConfigPath); + +export { defineConfig, blocksDevSetupBaseConfig }; + + + +// dev.setup/configs/eslint.helper.mjs +// import { defineConfig } from 'eslint/config'; +// import blocksDevSetupBaseConfig from '../eslint.config.mjs'; + +// export { defineConfig, blocksDevSetupBaseConfig }; \ No newline at end of file diff --git a/configs/lint-staged.config.mjs b/configs/lint-staged.config.mjs new file mode 100644 index 0000000..3d4f6c2 --- /dev/null +++ b/configs/lint-staged.config.mjs @@ -0,0 +1,23 @@ + +import { createRequire } from 'module'; +import path from 'path'; + +const require = createRequire(import.meta.url); +const resolveBin = (pkgName, binRelativePath) => { + const pkgRoot = path.dirname(require.resolve(`${pkgName}/package.json`)); + return `node ${path.join(pkgRoot, binRelativePath)}`; +}; + +const eslint = resolveBin('eslint', 'bin/eslint.js'); +const prettier = resolveBin('prettier', 'bin/prettier.cjs'); + +export default { + '*.{mjs,js,ts}': [ + // We use absolute paths to binaries to avoid ENOENT + `${eslint} --fix --no-warn-ignored`, + `${prettier} --write` + ], + '*.{html,css,scss}': [ + `${prettier} --write` + ] +}; diff --git a/eslint.config.mjs b/eslint.config.mjs index d9d7067..d92b06c 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -6,7 +6,7 @@ import { defineConfig } from 'eslint/config'; import tseslint from 'typescript-eslint'; import eslintConfigPrettier from 'eslint-config-prettier/flat'; -export default defineConfig( +const blocksDevSetupBaseConfig = defineConfig( eslint.configs.recommended, tseslint.configs.recommended, tseslint.configs.stylistic, @@ -19,3 +19,5 @@ export default defineConfig( }, eslintConfigPrettier, ); + +export default blocksDevSetupBaseConfig; diff --git a/package.json b/package.json index 27a4485..5ec168b 100644 --- a/package.json +++ b/package.json @@ -5,12 +5,14 @@ "type": "module", "main": "eslint.config.mjs", "bin": { + "blocks": "./bin/cli.js", "husky-ls-config-init": "bin/user-app.husky.lint-staged.js", "internal-husky-ls-init": "bin/internal.husky.lint-staged.js" }, "exports": { ".": "./eslint.config.mjs", - "./prettier": "./prettier.config.mjs" + "./prettier": "./prettier.config.mjs", + "./config": "./configs/eslint.helper.mjs" }, "publishConfig": { "registry": "https://registry.npmjs.org/" @@ -18,7 +20,7 @@ "scripts": { "eslint:lint": "eslint .", "prettier:format": "prettier --write \"**/*.{js,ts,css,html}\"", - "prepare": "husky" + "prepare": "node ./bin/cli.js setup-git" }, "dependencies": { "@eslint/js": "^10.0.1",