es-guard 1.7.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -5,6 +5,7 @@ import packageJson from "../package.json" with { type: "json" };
5
5
  import { checkCompatibility, formatViolationMessage } from "./lib/checkCompatiblity.js";
6
6
  import { getBrowserTargetsFromString } from "./lib/getBrowserTargets.js";
7
7
  import { detectProjectConfig, getConfigFileNames } from "./lib/detectTarget.js";
8
+ import { getCurrentProjectType } from "./lib/projectType.js";
8
9
  import { setVerboseMode } from "./lib/globalState.js";
9
10
  const version = packageJson.version;
10
11
  // Create the main program
@@ -18,6 +19,7 @@ program
18
19
  .option("-t, --target <version>", "Target ES version (2015, 2016, 2017, etc. or 6, 7, 8, etc. or 'latest'). If not specified, will auto-detect from project config files.")
19
20
  .option("-b, --browsers <targets>", "Browser targets for compatibility checking (optional: auto-determined from target)")
20
21
  .option("-v, --verbose", "Enable verbose output showing detailed detection process and configuration information")
22
+ .option("--skip", "Do not exit with error code when compatibility errors are found")
21
23
  .addHelpText("after", `
22
24
 
23
25
  Examples:
@@ -28,6 +30,7 @@ Examples:
28
30
  es-guard -t latest build # Check 'build' directory with latest ES (auto-determined browsers)
29
31
  es-guard --target 2017 --browsers "> 0.5%, last 2 versions" dist
30
32
  es-guard --verbose # Auto-detect with detailed detection information
33
+ es-guard --skip # Auto-detect and continue even if compatibility errors are found
31
34
 
32
35
  Auto-detection searches for ES target in:
33
36
  - package.json (browserslist field)
@@ -51,7 +54,7 @@ Browser targets use Browserslist format:
51
54
 
52
55
  Exit codes:
53
56
  0 - No compatibility issues found
54
- 1 - Compatibility issues found or error occurred
57
+ 1 - Compatibility issues found or error occurred (unless --skip is used)
55
58
  `);
56
59
  // Add validation for the target option
57
60
  program.hook("preAction", (thisCommand) => {
@@ -80,6 +83,9 @@ program.action(async (directory, options) => {
80
83
  if (options.verbose) {
81
84
  console.log("🔍 Auto-detecting project configuration...");
82
85
  console.log(`📂 Searching in: ${process.cwd()}`);
86
+ // Detect and log project type
87
+ const projectType = getCurrentProjectType();
88
+ console.log(`🏗️ Project type: ${projectType}`);
83
89
  console.log("");
84
90
  }
85
91
  // Use unified detection to get all configuration at once
@@ -210,13 +216,17 @@ program.action(async (directory, options) => {
210
216
  }
211
217
  }
212
218
  if (errors.length > 0) {
213
- process.exit(1);
214
- }
215
- else {
216
- console.log("✅ No compatibility errors found!");
217
- if (warnings.length > 0) {
218
- console.log("⚠️ There are compat warnings, but no syntax errors. Please address these warnings and update polyfills in your app and in es-guard config.");
219
+ if (options.skip) {
220
+ console.log("⚠️ Compatibility errors found, but continuing due to --skip option");
221
+ process.exit(0);
219
222
  }
223
+ else {
224
+ process.exit(1);
225
+ }
226
+ }
227
+ console.log("✅ No compatibility errors found!");
228
+ if (warnings.length > 0) {
229
+ console.log("⚠️ There are compat warnings, but no syntax errors. Please address these warnings and update polyfills in your app and in es-guard config.");
220
230
  }
221
231
  }
222
232
  catch (error) {
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Default configurations for different project types
3
+ */
4
+ /**
5
+ * Next.js default browserslist configuration
6
+ * Source: https://nextjs.org/docs/basic-features/supported-browsers-features
7
+ */
8
+ export const NEXTJS_DEFAULT_BROWSERSLIST = ["chrome 64", "edge 79", "firefox 67", "opera 51", "safari 12"];
9
+ /**
10
+ * Default output directories for different project types
11
+ */
12
+ export const DEFAULT_OUTPUT_DIRS = {
13
+ nextjs: ".next/static",
14
+ vite: "dist",
15
+ webpack: "dist",
16
+ rollup: "dist",
17
+ parcel: "dist",
18
+ generic: "dist",
19
+ };
20
+ /**
21
+ * Project type detection helpers
22
+ */
23
+ export const PROJECT_TYPES = {
24
+ nextjs: "nextjs",
25
+ vite: "vite",
26
+ webpack: "webpack",
27
+ rollup: "rollup",
28
+ parcel: "parcel",
29
+ generic: "generic",
30
+ };
31
+ export const ProjectTypeKeys = new Set(Object.keys(PROJECT_TYPES));
32
+ export const isProjectType = (projectType) => {
33
+ return ProjectTypeKeys.has(projectType);
34
+ };
35
+ /**
36
+ * Get default browserslist for a project type
37
+ */
38
+ export function getDefaultBrowserslist(projectType) {
39
+ switch (projectType) {
40
+ case PROJECT_TYPES.nextjs:
41
+ return [...NEXTJS_DEFAULT_BROWSERSLIST];
42
+ // Add more project types here as needed
43
+ default:
44
+ return null;
45
+ }
46
+ }
47
+ /**
48
+ * Get default output directory for a project type
49
+ */
50
+ export function getDefaultOutputDir(projectType) {
51
+ if (isProjectType(projectType)) {
52
+ return DEFAULT_OUTPUT_DIRS[projectType];
53
+ }
54
+ return DEFAULT_OUTPUT_DIRS.generic;
55
+ }
56
+ /**
57
+ * Detect project type from package.json dependencies
58
+ */
59
+ export function detectProjectType(dependencies = {}, devDependencies = {}) {
60
+ const allDeps = { ...dependencies, ...devDependencies };
61
+ if (allDeps.next) {
62
+ return PROJECT_TYPES.nextjs;
63
+ }
64
+ if (allDeps.vite) {
65
+ return PROJECT_TYPES.vite;
66
+ }
67
+ if (allDeps.webpack) {
68
+ return PROJECT_TYPES.webpack;
69
+ }
70
+ if (allDeps.rollup) {
71
+ return PROJECT_TYPES.rollup;
72
+ }
73
+ if (allDeps.parcel) {
74
+ return PROJECT_TYPES.parcel;
75
+ }
76
+ return PROJECT_TYPES.generic;
77
+ }
@@ -1,6 +1,9 @@
1
1
  import * as fs from "fs";
2
2
  import * as path from "path";
3
3
  import { verboseMode } from "./globalState.js";
4
+ import { NEXTJS_DEFAULT_BROWSERSLIST } from "./defaults.js";
5
+ import { getCurrentProjectType } from "./projectType.js";
6
+ import { readJsonFile, readTextFile, evaluateJsFile, isPackageJson, isTsConfig, isBabelRc, isViteConfig, isWebpackConfig, isNextConfig, } from "./utils.js";
4
7
  // Shared utilities for ES version parsing and conversion
5
8
  const CONFIG_FILE_NAMES = [
6
9
  "package.json",
@@ -52,80 +55,6 @@ const TARGET_MAP = {
52
55
  es6: "2015",
53
56
  es5: "2009",
54
57
  };
55
- /**
56
- * Helper to read and parse JSON file safely
57
- */
58
- const readJsonFile = (filePath) => {
59
- const content = fs.readFileSync(filePath, "utf-8");
60
- return JSON.parse(content);
61
- };
62
- /**
63
- * Helper to read text file safely
64
- */
65
- const readTextFile = (filePath) => {
66
- return fs.readFileSync(filePath, "utf-8");
67
- };
68
- /**
69
- * Helper to safely evaluate JavaScript files (for config files)
70
- */
71
- const evaluateJsFile = (filePath) => {
72
- const content = readTextFile(filePath);
73
- // Create a safe evaluation context
74
- const module = { exports: {} };
75
- const require = (id) => {
76
- if (id === "path")
77
- return path;
78
- if (id === "fs")
79
- return fs;
80
- throw new Error(`Cannot require '${id}' in config evaluation`);
81
- };
82
- try {
83
- // Use Function constructor to create a safe evaluation environment
84
- const fn = new Function("module", "exports", "require", "path", "fs", "__dirname", content);
85
- fn(module, module.exports, require, path, fs, path.dirname(filePath));
86
- return module.exports;
87
- }
88
- catch (error) {
89
- console.warn(`Error evaluating ${filePath}:`, error);
90
- return null;
91
- }
92
- };
93
- /**
94
- * Type guard for package.json structure
95
- */
96
- const isPackageJson = (obj) => {
97
- return typeof obj === "object" && obj !== null;
98
- };
99
- /**
100
- * Type guard for tsconfig.json structure
101
- */
102
- const isTsConfig = (obj) => {
103
- return typeof obj === "object" && obj !== null && "compilerOptions" in obj;
104
- };
105
- /**
106
- * Type guard for .babelrc structure
107
- */
108
- const isBabelRc = (obj) => {
109
- return typeof obj === "object" && obj !== null && "presets" in obj;
110
- };
111
- /**
112
- * Type guard for vite config structure
113
- */
114
- const isViteConfig = (obj) => {
115
- return typeof obj === "object" && obj !== null;
116
- };
117
- /**
118
- * Type guard for webpack config structure
119
- */
120
- const isWebpackConfig = (obj) => {
121
- return typeof obj === "object" && obj !== null;
122
- };
123
- /**
124
- * Type guard for next.js config structure
125
- */
126
- const isNextConfig = (obj) => {
127
- return typeof obj === "object" && obj !== null;
128
- };
129
58
  /**
130
59
  * Get parser function for a given config file
131
60
  */
@@ -212,7 +141,7 @@ export const detectProjectConfig = (cwd = process.cwd()) => {
212
141
  // Update output directory if found and not already set
213
142
  if (detection.outputDir && !result.outputDir) {
214
143
  result.outputDir = detection.outputDir;
215
- result.outputSource = filename;
144
+ result.outputSource = detection.outputSource || filename;
216
145
  if (verboseMode) {
217
146
  console.log(` 📁 Output directory: ${detection.outputDir}`);
218
147
  }
@@ -226,7 +155,7 @@ export const detectProjectConfig = (cwd = process.cwd()) => {
226
155
  // Update browserslist if found and not already set
227
156
  if (detection.browserslist && !result.browserslist) {
228
157
  result.browserslist = detection.browserslist;
229
- result.browserslistSource = filename;
158
+ result.browserslistSource = detection.browserslistSource || filename;
230
159
  if (verboseMode) {
231
160
  console.log(` 🌐 Browserslist: ${detection.browserslist.join(", ")} (from ${result.browserslistSource})`);
232
161
  }
@@ -335,6 +264,8 @@ const parsePackageJson = (filePath) => {
335
264
  // Store the full browserslist for CLI defaults
336
265
  result.browserslist = browserslist.filter((browser) => typeof browser === "string");
337
266
  }
267
+ // Use global project type detection (lazy initialization)
268
+ const projectType = getCurrentProjectType(path.dirname(filePath));
338
269
  // Check for output directory hints
339
270
  if (pkg.dist) {
340
271
  result.outputDir = pkg.dist;
@@ -345,9 +276,15 @@ const parsePackageJson = (filePath) => {
345
276
  else if (pkg.main && pkg.main.startsWith("./dist/")) {
346
277
  result.outputDir = "dist";
347
278
  }
348
- else if (pkg.dependencies?.next || pkg.devDependencies?.next) {
349
- // Next.js apps default to .next directory
279
+ else if (projectType === "nextjs") {
280
+ // Set default output directory for Next.js projects
350
281
  result.outputDir = ".next/static";
282
+ result.outputSource = "package.json (default)";
283
+ }
284
+ // If no browserslist was found, use default for detected project type
285
+ if (!result.browserslist && projectType === "nextjs") {
286
+ result.browserslist = [...NEXTJS_DEFAULT_BROWSERSLIST];
287
+ result.browserslistSource = "Next.js default";
351
288
  }
352
289
  return result;
353
290
  };
@@ -1,7 +1,9 @@
1
- // Global state management for CLI options
1
+ // Global state management for CLI options and project configuration
2
2
  // Static global state variables
3
3
  export let verboseMode = false;
4
4
  export let debugMode = false;
5
+ export let projectType = undefined;
6
+ export let projectTypeDetected = false;
5
7
  /**
6
8
  * Set the global verbose mode
7
9
  */
@@ -21,6 +23,8 @@ export const getGlobalState = () => {
21
23
  return {
22
24
  verbose: verboseMode,
23
25
  debug: debugMode,
26
+ projectType,
27
+ projectTypeDetected,
24
28
  };
25
29
  };
26
30
  /**
@@ -31,6 +35,10 @@ export const setGlobalState = (options) => {
31
35
  verboseMode = options.verbose;
32
36
  if (options.debug !== undefined)
33
37
  debugMode = options.debug;
38
+ if (options.projectType !== undefined)
39
+ projectType = options.projectType;
40
+ if (options.projectTypeDetected !== undefined)
41
+ projectTypeDetected = options.projectTypeDetected;
34
42
  };
35
43
  /**
36
44
  * Reset global state to defaults
@@ -38,4 +46,25 @@ export const setGlobalState = (options) => {
38
46
  export const resetGlobalState = () => {
39
47
  verboseMode = false;
40
48
  debugMode = false;
49
+ projectType = undefined;
50
+ projectTypeDetected = false;
51
+ };
52
+ /**
53
+ * Set the detected project type globally
54
+ */
55
+ export const setProjectType = (detectedType) => {
56
+ projectType = detectedType;
57
+ projectTypeDetected = true;
58
+ };
59
+ /**
60
+ * Get the detected project type (lazy detection if not already done)
61
+ */
62
+ export const getProjectType = () => {
63
+ return projectType;
64
+ };
65
+ /**
66
+ * Check if project type has been detected
67
+ */
68
+ export const isProjectTypeDetected = () => {
69
+ return projectTypeDetected;
41
70
  };
@@ -0,0 +1,46 @@
1
+ import * as fs from "fs";
2
+ import * as path from "path";
3
+ import { verboseMode, setProjectType, getProjectType, isProjectTypeDetected } from "./globalState.js";
4
+ import { detectProjectType } from "./defaults.js";
5
+ import { readJsonFile, isPackageJson } from "./utils.js";
6
+ /**
7
+ * Detect and cache project type globally (lazy initialization)
8
+ */
9
+ export const detectAndCacheProjectType = (cwd = process.cwd()) => {
10
+ // Return cached result if already detected
11
+ if (isProjectTypeDetected()) {
12
+ return getProjectType() || null;
13
+ }
14
+ // Detect project type from package.json
15
+ const packageJsonPath = path.join(cwd, "package.json");
16
+ if (fs.existsSync(packageJsonPath)) {
17
+ try {
18
+ const pkg = readJsonFile(packageJsonPath);
19
+ if (isPackageJson(pkg)) {
20
+ const detectedType = detectProjectType(pkg.dependencies, pkg.devDependencies);
21
+ setProjectType(detectedType);
22
+ if (verboseMode) {
23
+ console.log(`🔍 Project type detected: ${detectedType}`);
24
+ }
25
+ return detectedType;
26
+ }
27
+ }
28
+ catch (error) {
29
+ // Ignore errors when detecting project type
30
+ if (verboseMode) {
31
+ console.log(` ⚠️ Error detecting project type: ${error instanceof Error ? error.message : String(error)}`);
32
+ }
33
+ }
34
+ }
35
+ // No project type detected
36
+ if (verboseMode) {
37
+ console.log(`🔍 No project type detected`);
38
+ }
39
+ return null;
40
+ };
41
+ /**
42
+ * Get the current project type (detects if not already cached)
43
+ */
44
+ export const getCurrentProjectType = (cwd = process.cwd()) => {
45
+ return detectAndCacheProjectType(cwd) || "generic";
46
+ };
@@ -0,0 +1,76 @@
1
+ import * as fs from "fs";
2
+ import * as path from "path";
3
+ /**
4
+ * Helper to read and parse JSON file safely
5
+ */
6
+ export const readJsonFile = (filePath) => {
7
+ const content = fs.readFileSync(filePath, "utf-8");
8
+ return JSON.parse(content);
9
+ };
10
+ /**
11
+ * Helper to read text file safely
12
+ */
13
+ export const readTextFile = (filePath) => {
14
+ return fs.readFileSync(filePath, "utf-8");
15
+ };
16
+ /**
17
+ * Helper to safely evaluate JavaScript files (for config files)
18
+ */
19
+ export const evaluateJsFile = (filePath) => {
20
+ const content = readTextFile(filePath);
21
+ // Create a safe evaluation context
22
+ const module = { exports: {} };
23
+ const require = (id) => {
24
+ if (id === "path")
25
+ return path;
26
+ if (id === "fs")
27
+ return fs;
28
+ throw new Error(`Cannot require '${id}' in config evaluation`);
29
+ };
30
+ try {
31
+ // Use Function constructor to create a safe evaluation environment
32
+ const fn = new Function("module", "exports", "require", "path", "fs", "__dirname", content);
33
+ fn(module, module.exports, require, path, fs, path.dirname(filePath));
34
+ return module.exports;
35
+ }
36
+ catch (error) {
37
+ console.warn(`Error evaluating ${filePath}:`, error);
38
+ return null;
39
+ }
40
+ };
41
+ /**
42
+ * Type guard for package.json structure
43
+ */
44
+ export const isPackageJson = (obj) => {
45
+ return typeof obj === "object" && obj !== null;
46
+ };
47
+ /**
48
+ * Type guard for tsconfig.json structure
49
+ */
50
+ export const isTsConfig = (obj) => {
51
+ return typeof obj === "object" && obj !== null && "compilerOptions" in obj;
52
+ };
53
+ /**
54
+ * Type guard for .babelrc structure
55
+ */
56
+ export const isBabelRc = (obj) => {
57
+ return typeof obj === "object" && obj !== null && "presets" in obj;
58
+ };
59
+ /**
60
+ * Type guard for vite config structure
61
+ */
62
+ export const isViteConfig = (obj) => {
63
+ return typeof obj === "object" && obj !== null;
64
+ };
65
+ /**
66
+ * Type guard for webpack config structure
67
+ */
68
+ export const isWebpackConfig = (obj) => {
69
+ return typeof obj === "object" && obj !== null;
70
+ };
71
+ /**
72
+ * Type guard for next.js config structure
73
+ */
74
+ export const isNextConfig = (obj) => {
75
+ return typeof obj === "object" && obj !== null;
76
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "es-guard",
3
- "version": "1.7.0",
3
+ "version": "1.9.0",
4
4
  "description": "A tool to check JavaScript compatibility with target environments",
5
5
  "type": "module",
6
6
  "main": "dist/cli.js",