Skip to content

Commit

Permalink
refactor(cli): rewrites config scaffolding without handlebars
Browse files Browse the repository at this point in the history
  • Loading branch information
sverweij committed Aug 13, 2023
1 parent 30ea30f commit af41341
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 101 deletions.
3 changes: 1 addition & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
.SUFFIXES: .js .css .html
NODE=node
RM=rm -f
GENERATED_SOURCES=src/cli/init-config/config.js.template.js \
src/report/dot/dot.template.js \
GENERATED_SOURCES=src/report/dot/dot.template.js \
src/schema/baseline-violations.schema.mjs \
src/schema/configuration.schema.mjs \
src/schema/cruise-result.schema.mjs \
Expand Down
174 changes: 156 additions & 18 deletions src/cli/init-config/build-config.mjs
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
// @ts-check
import Handlebars from "handlebars/runtime.js";
import { folderNameArrayToRE } from "./utl.mjs";

/* eslint import/no-unassigned-import: 0 */
await import("./config.js.template.js");
import configTemplate from "./config-template.mjs";

/**
* @param {string} pString
Expand All @@ -24,25 +21,166 @@ function extensionsToString(pExtensions) {
return "";
}

/**
* @param {import("./types.js").IInitConfig} pInitOptions
* @returns {string}
*/
function buildNotToTestRule(pInitOptions) {
const lNotToTestRule = `{
name: 'not-to-test',
comment:
"This module depends on code within a folder that should only contain tests. As tests don't " +
"implement functionality this is odd. Either you're writing a test outside the test folder " +
"or there's something in the test folder that isn't a test.",
severity: 'error',
from: {
pathNot: '{{testLocationRE}}'
},
to: {
path: '{{testLocationRE}}'
}
},`;
return pInitOptions.hasTestsOutsideSource
? lNotToTestRule.replace(
/{{testLocationRE}}/g,
folderNameArrayToRE(pInitOptions?.testLocation ?? []),
)
: "";
}

/**
* @param {import("./types.js").IInitConfig} pInitOptions
* @returns {string}
*/
function buildTsPreCompilationDepsAttribute(pInitOptions) {
return pInitOptions.tsPreCompilationDeps
? "tsPreCompilationDeps: true,"
: "// tsPreCompilationDeps: false,";
}

/**
* @param {import("./types.js").IInitConfig} pInitOptions
* @returns {string}
*/
function buildCombinedDependenciesAttribute(pInitOptions) {
return pInitOptions.combinedDependencies
? "combinedDependencies: true,"
: "// combinedDependencies: false,";
}

/**
* @param {import("./types.js").IInitConfig} pInitOptions
* @returns {string}
*/
function buildTsOrJsConfigAttribute(pInitOptions) {
if (pInitOptions.useTsConfig) {
return `tsConfig: {
fileName: '${pInitOptions.tsConfig}'
},`;
}
if (pInitOptions.useJsConfig) {
return `tsConfig: {
fileName: '${pInitOptions.jsConfig}'
},`;
}
return `// tsConfig: {
// fileName: 'tsconfig.json'
// },`;
}

/**
* @param {import("./types.js").IInitConfig} pInitOptions
* @returns {string}
*/
function buildWebpackConfigAttribute(pInitOptions) {
return pInitOptions.webpackConfig
? `webpackConfig: {
fileName: '${pInitOptions.webpackConfig}',
// env: {},
// arguments: {}
},`
: `// webpackConfig: {
// fileName: 'webpack.config.js',
// env: {},
// arguments: {}
// },`;
}

/**
* @param {import("./types.js").IInitConfig} pInitOptions
* @returns {string}
*/
function buildBabelConfigAttribute(pInitOptions) {
return pInitOptions.babelConfig
? `babelConfig: {
fileName: '${pInitOptions.babelConfig}'
},`
: `// babelConfig: {
// fileName: '.babelrc',
// },`;
}

/**
* @param {import("./types.js").IInitConfig} pInitOptions
* @returns {string}
*/
function buildExtensionsAttribute(pInitOptions) {
return pInitOptions.specifyResolutionExtensions
? `extensions: ${extensionsToString(pInitOptions.resolutionExtensions)},`
: `// extensions: [".js", ".jsx", ".ts", ".tsx", ".d.ts"],`;
}

/**
* @param {import("./types.js").IInitConfig} pInitOptions
* @returns {string}
*/
function buildMainFieldsAttribute(pInitOptions) {
return pInitOptions.usesTypeScript
? `mainFields: ["main", "types"],`
: `// mainFields: ["main", "types"],`;
}

/**
* Creates a .dependency-cruiser config with a set of basic validations
* to the current directory.
*
* @param {import("./types.js").IInitConfig} pNormalizedInitOptions Options that influence the shape of
* @param {import("./types.js").IInitConfig} pInitOptions ('Normalized') options that influence the shape of
* the configuration
* @returns {string} the configuration as a string
*/

export default function buildConfig(pNormalizedInitOptions) {
return Handlebars.templates["config.js.template.hbs"]({
...pNormalizedInitOptions,

sourceLocationRE: folderNameArrayToRE(
pNormalizedInitOptions.sourceLocation
),
testLocationRE: folderNameArrayToRE(pNormalizedInitOptions.testLocation),
resolutionExtensionsAsString: extensionsToString(
pNormalizedInitOptions.resolutionExtensions
),
});
export default function buildConfig(pInitOptions) {
return configTemplate
.replace(
/{{sourceLocationRE}}/g,
folderNameArrayToRE(pInitOptions.sourceLocation),
)
.replace(
/{{resolutionExtensionsAsString}}/g,
extensionsToString(pInitOptions.resolutionExtensions),
)
.replace("{{notToTestRule}}", buildNotToTestRule(pInitOptions))
.replace(
"{{tsPreCompilationDepsAttribute}}",
buildTsPreCompilationDepsAttribute(pInitOptions),
)
.replace(
"{{combinedDependenciesAttribute}}",
buildCombinedDependenciesAttribute(pInitOptions),
)
.replace(
"{{tsOrJsConfigAttribute}}",
buildTsOrJsConfigAttribute(pInitOptions),
)
.replace(
"{{webpackConfigAttribute}}",
buildWebpackConfigAttribute(pInitOptions),
)
.replace(
"{{babelConfigAttribute}}",
buildBabelConfigAttribute(pInitOptions),
)
.replace("{{extensionsAttribute}}", buildExtensionsAttribute(pInitOptions))
.replace("{{mainFieldsAttribute}}", buildMainFieldsAttribute(pInitOptions))
.replace("{{version}}", pInitOptions.version)
.replace("{{date}}", pInitOptions.date);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/** @type {import('dependency-cruiser').IConfiguration} */
/* eslint-disable max-lines, no-useless-escape */
export default `/** @type {import('dependency-cruiser').IConfiguration} */
module.exports = {
forbidden: [
{
Expand Down Expand Up @@ -125,22 +126,7 @@ module.exports = {
},
/* rules you might want to tweak for your specific situation: */
{{#hasTestsOutsideSource}}
{
name: 'not-to-test',
comment:
"This module depends on code within a folder that should only contain tests. As tests don't " +
"implement functionality this is odd. Either you're writing a test outside the test folder " +
"or there's something in the test folder that isn't a test.",
severity: 'error',
from: {
pathNot: '{{testLocationRE}}'
},
to: {
path: '{{testLocationRE}}'
}
},
{{/hasTestsOutsideSource}}
{{notToTestRule}}
{
name: 'not-to-spec',
comment:
Expand Down Expand Up @@ -239,7 +225,7 @@ module.exports = {
// moduleSystems: ['amd', 'cjs', 'es6', 'tsd'],
/* prefix for links in html and svg output (e.g. 'https://github.com/you/yourrepo/blob/develop/'
to open it on your online repo or `vscode://file/${process.cwd()}/` to
to open it on your online repo or \`vscode://file/$\{process.cwd()}/\` to
open it in visual studio code),
*/
// prefix: '',
Expand All @@ -248,11 +234,7 @@ module.exports = {
true: also detect dependencies that only exist before typescript-to-javascript compilation
"specify": for each dependency identify whether it only exists before compilation or also after
*/
{{#if tsPreCompilationDeps}}
tsPreCompilationDeps: true,
{{^}}
// tsPreCompilationDeps: false,
{{/if}}
{{tsPreCompilationDepsAttribute}}
/*
list of extensions to scan that aren't javascript or compile-to-javascript.
Expand All @@ -265,11 +247,7 @@ module.exports = {
folder the cruise is initiated from. Useful for how (some) mono-repos
manage dependencies & dependency definitions.
*/
{{#if combinedDependencies}}
combinedDependencies: true,
{{^}}
// combinedDependencies: false,
{{/if}}
{{combinedDependenciesAttribute}}
/* if true leave symlinks untouched, otherwise use the realpath */
// preserveSymlinks: false,
Expand All @@ -282,61 +260,27 @@ module.exports = {
dependency-cruiser's current working directory). When not provided
defaults to './tsconfig.json'.
*/
{{#if useTsConfig}}
tsConfig: {
fileName: '{{tsConfig}}'
},
{{^}}
{{#if useJsConfig}}
tsConfig: {
fileName: '{{jsConfig}}'
},
{{^}}
// tsConfig: {
// fileName: './tsconfig.json'
// },
{{/if}}
{{/if}}
{{tsOrJsConfigAttribute}}
/* Webpack configuration to use to get resolve options from.
The (optional) fileName attribute specifies which file to take (relative
to dependency-cruiser's current working directory. When not provided defaults
to './webpack.conf.js'.
The (optional) `env` and `arguments` attributes contain the parameters to be passed if
The (optional) \`env\` and \`arguments\` attributes contain the parameters to be passed if
your webpack config is a function and takes them (see webpack documentation
for details)
*/
{{#if useWebpackConfig}}
webpackConfig: {
fileName: '{{webpackConfig}}',
// env: {},
// arguments: {},
},
{{^}}
// webpackConfig: {
// fileName: './webpack.config.js',
// env: {},
// arguments: {},
// },
{{/if}}
{{webpackConfigAttribute}}
/* Babel config ('.babelrc', '.babelrc.json', '.babelrc.json5', ...) to use
for compilation (and whatever other naughty things babel plugins do to
source code). This feature is well tested and usable, but might change
behavior a bit over time (e.g. more precise results for used module
systems) without dependency-cruiser getting a major version bump.
*/
{{#if useBabelConfig}}
babelConfig: {
fileName: '{{babelConfig}}'
},
{{^}}
// babelConfig: {
// fileName: './.babelrc'
// },
{{/if}}
{{babelConfigAttribute}}
/* List of strings you have in use in addition to cjs/ es6 requires
& imports to declare module dependencies. Use this e.g. if you've
Expand All @@ -356,13 +300,13 @@ module.exports = {
['exports'] when you use packages that use such a field and your environment
supports it (e.g. node ^12.19 || >=14.7 or recent versions of webpack).
If you have an `exportsFields` attribute in your webpack config, that one
If you have an \`exportsFields\` attribute in your webpack config, that one
will have precedence over the one specified here.
*/
exportsFields: ["exports"],
/* List of conditions to check for in the exports field. e.g. use ['imports']
if you're only interested in exposed es6 modules, ['require'] for commonjs,
or all conditions at once `(['import', 'require', 'node', 'default']`)
or all conditions at once \`(['import', 'require', 'node', 'default']\`)
if anything goes for you. Only works when the 'exportsFields' array is
non-empty.
Expand All @@ -372,18 +316,14 @@ module.exports = {
conditionNames: ["import", "require", "node", "default"],
/*
The extensions, by default are the same as the ones dependency-cruiser
can access (run `npx depcruise --info` to see which ones that are in
can access (run \`npx depcruise --info\` to see which ones that are in
_your_ environment. If that list is larger than what you need (e.g.
it contains .js, .jsx, .ts, .tsx, .cts, .mts - but you don't use
TypeScript you can pass just the extensions you actually use (e.g.
[".js", ".jsx"]). This can speed up the most expensive step in
dependency cruising (module resolution) quite a bit.
*/
{{#if specifyResolutionExtensions}}
extensions: {{{resolutionExtensionsAsString}}},
{{^}}
// extensions: [".js", ".jsx", ".ts", ".tsx", ".d.ts"],
{{/if}}
{{extensionsAttribute}}
/*
If your TypeScript project makes use of types specified in 'types'
fields in package.jsons of external dependencies, specify "types"
Expand All @@ -392,11 +332,7 @@ module.exports = {
this if you're not sure, but still use TypeScript. In a future version
of dependency-cruiser this will likely become the default.
*/
{{#if usesTypeScript}}
mainFields: ["main", "types"],
{{^}}
// mainFields: ["main", "types"],
{{/if}}
{{mainFieldsAttribute}}
},
reporterOptions: {
dot: {
Expand Down Expand Up @@ -483,7 +419,7 @@ module.exports = {
archi: {
/* pattern of modules that can be consolidated in the high level
graphical dependency graph. If you use the high level graphical
dependency graph reporter (`archi`) you probably want to tweak
dependency graph reporter (\`archi\`) you probably want to tweak
this collapsePattern to your situation.
*/
collapsePattern: '^(packages|src|lib|app|bin|test(s?)|spec(s?))/[^/]+|node_modules/(@[^/]+/[^/]+|[^/]+)',
Expand All @@ -504,3 +440,4 @@ module.exports = {
}
};
// generated: dependency-cruiser@{{version}} on {{date}}
`;
Loading

0 comments on commit af41341

Please sign in to comment.