From f9fea67fc5aa8b6e4dcdc8b1bd9af8db2e22e62b Mon Sep 17 00:00:00 2001 From: Jason Dent Date: Thu, 13 Jul 2023 11:53:07 +0200 Subject: [PATCH] fix: support automatic creation of `cspell-tools.config.yaml` (#4631) --- packages/cspell-tools/package.json | 3 +- packages/cspell-tools/src/AppOptions.ts | 5 +- packages/cspell-tools/src/app.ts | 14 +++-- packages/cspell-tools/src/compile.test.ts | 38 +++++++++++++ packages/cspell-tools/src/compile.ts | 28 ++++++++-- .../createCompileRequest.test.ts.snap | 54 +++++++++++++++++++ .../src/compiler/createCompileRequest.test.ts | 3 ++ .../src/compiler/createCompileRequest.ts | 18 +++++-- pnpm-lock.yaml | 5 +- 9 files changed, 155 insertions(+), 13 deletions(-) create mode 100644 packages/cspell-tools/src/compile.test.ts diff --git a/packages/cspell-tools/package.json b/packages/cspell-tools/package.json index 89a2519f56b3..94f4d4581291 100644 --- a/packages/cspell-tools/package.json +++ b/packages/cspell-tools/package.json @@ -56,7 +56,8 @@ "cspell-trie-lib": "workspace:*", "gensequence": "^5.0.2", "glob": "^10.3.3", - "hunspell-reader": "workspace:*" + "hunspell-reader": "workspace:*", + "yaml": "^2.3.1" }, "engines": { "node": ">=16" diff --git a/packages/cspell-tools/src/AppOptions.ts b/packages/cspell-tools/src/AppOptions.ts index 1336d5ab1acf..9dc1b989452d 100644 --- a/packages/cspell-tools/src/AppOptions.ts +++ b/packages/cspell-tools/src/AppOptions.ts @@ -7,7 +7,7 @@ export interface CompileCommonAppOptions { max_depth?: string; maxDepth?: string; merge?: string; - experimental: string[]; + experimental?: string[]; split?: boolean; sort?: boolean; keepRawCase?: boolean; @@ -15,7 +15,10 @@ export interface CompileCommonAppOptions { trie3?: boolean; trie4?: boolean; trieBase?: string; + listFile?: string[]; useLegacySplitter?: boolean; + /** Indicate that a config file should be created instead of building. */ + init?: boolean; } export interface CompileAppOptions extends CompileCommonAppOptions { sort: boolean; diff --git a/packages/cspell-tools/src/app.ts b/packages/cspell-tools/src/app.ts index d25db1d365f8..aa59c8faa3ed 100644 --- a/packages/cspell-tools/src/app.ts +++ b/packages/cspell-tools/src/app.ts @@ -44,7 +44,15 @@ function addCompileOptions(compileCommand: program.Command): program.Command { ) .option('--trie3', 'Use file format trie3', false) .option('--trie4', 'Use file format trie4', false) - .option('--trie-base ', 'Advanced: Set the trie base number. A value between 10 and 36'); + .option('--trie-base ', 'Advanced: Set the trie base number. A value between 10 and 36') + .option( + '--list-file ', + 'Path to a file that contains the list of files to compile. A list file contains one file per line.' + ) + .option( + '--init', + 'Create a build command `cspell-tools.config.yaml` file based upon the options given instead of building.' + ); } interface ShasumOptions { @@ -77,7 +85,7 @@ export async function run(program: program.Command, argv: string[], flags?: Feat program.version(npmPackage.version); - addCompileOptions(program.command('compile ').description('Compile words into a cspell dictionary files.')) + addCompileOptions(program.command('compile [src...]').description('Compile words into a cspell dictionary files.')) .option('--trie', 'Compile into a trie file.', false) .option('--no-sort', 'Do not sort the result') .action((src: string[], options: CompileAppOptions) => { @@ -86,7 +94,7 @@ export async function run(program: program.Command, argv: string[], flags?: Feat addCompileOptions( program - .command('compile-trie ') + .command('compile-trie [src...]') .description( 'Compile words lists or Hunspell dictionary into trie files used by cspell.\nAlias of `compile --trie`' ) diff --git a/packages/cspell-tools/src/compile.test.ts b/packages/cspell-tools/src/compile.test.ts new file mode 100644 index 000000000000..263a43a7c7d8 --- /dev/null +++ b/packages/cspell-tools/src/compile.test.ts @@ -0,0 +1,38 @@ +import { writeFile } from 'node:fs/promises'; + +import { describe, expect, test, vi } from 'vitest'; + +import { processCompileAction } from './compile.js'; + +vi.mock('node:fs/promises', () => ({ + writeFile: vi.fn().mockImplementation(() => Promise.resolve(undefined)), +})); + +const mockWriteFile = vi.mocked(writeFile); + +vi.spyOn(console, 'log').mockImplementation(() => undefined); + +describe('compile', () => { + test('--init option', async () => { + const options = { + compress: true, + sort: true, + merge: 'public-licenses', + output: '.', + listFile: ['source-files.txt'], + init: true, + }; + const expected = `\ +targets: + - name: public-licenses + targetDirectory: . + compress: true + format: plaintext + sources: + - listFile: source-files.txt + sort: true +`; + await processCompileAction([], options, undefined); + expect(mockWriteFile).toHaveBeenLastCalledWith('cspell-tools.config.yaml', expected); + }); +}); diff --git a/packages/cspell-tools/src/compile.ts b/packages/cspell-tools/src/compile.ts index 427104742f18..3003be97d5e5 100644 --- a/packages/cspell-tools/src/compile.ts +++ b/packages/cspell-tools/src/compile.ts @@ -1,31 +1,41 @@ +import { writeFile } from 'node:fs/promises'; + import { opConcatMap, pipe } from '@cspell/cspell-pipe/sync'; +import YAML from 'yaml'; import type { CompileCommonAppOptions } from './AppOptions.js'; import { compile } from './compiler/compile.js'; import { createCompileRequest } from './compiler/createCompileRequest.js'; +import type { CompileRequest } from './config/config.js'; import type { FeatureFlags } from './FeatureFlags/index.js'; import { getSystemFeatureFlags, parseFlags } from './FeatureFlags/index.js'; import { globP } from './util/globP.js'; getSystemFeatureFlags().register('compound', 'Enable compound dictionary sources.'); +const defaultConfigFile = 'cspell-tools.config.yaml'; + export async function processCompileAction( src: string[], options: CompileCommonAppOptions, featureFlags: FeatureFlags | undefined ): Promise { const ff = featureFlags || getSystemFeatureFlags(); - parseFlags(ff, options.experimental); + parseFlags(ff, options.experimental || []); return useCompile(src, options); } async function useCompile(src: string[], options: CompileCommonAppOptions): Promise { console.log( - 'Compile:\n output: %s\n compress: %s\n files:\n %s \n\n', + 'Compile:\n output: %s\n compress: %s\n files:\n %s ', options.output || 'default', options.compress ? 'true' : 'false', src.join('\n ') ); + if (options.listFile && options.listFile.length) { + console.log(' list files:\n %s', options.listFile.join('\n ')); + } + console.log('\n\n'); const globResults = await Promise.all(src.map((s) => globP(s))); const sources = [ @@ -35,5 +45,17 @@ async function useCompile(src: string[], options: CompileCommonAppOptions): Prom ), ]; - return compile(createCompileRequest(sources, options)); + const request = createCompileRequest(sources, options); + return options.init ? initConfig(request) : compile(request); +} + +async function initConfig(request: CompileRequest): Promise { + const content = YAML.stringify(request, null, 2); + console.log('Writing config file: %s', defaultConfigFile); + await writeFile(defaultConfigFile, content); + + console.log(`Init complete. +To build, use: + cspell-tools-cli build +`); } diff --git a/packages/cspell-tools/src/compiler/__snapshots__/createCompileRequest.test.ts.snap b/packages/cspell-tools/src/compiler/__snapshots__/createCompileRequest.test.ts.snap index bf818d380113..ddf08ade5403 100644 --- a/packages/cspell-tools/src/compiler/__snapshots__/createCompileRequest.test.ts.snap +++ b/packages/cspell-tools/src/compiler/__snapshots__/createCompileRequest.test.ts.snap @@ -63,3 +63,57 @@ exports[`createCompileRequest > createCompileRequest 4`] = ` ], } `; + +exports[`createCompileRequest > createCompileRequest 5`] = ` +{ + "targets": [ + { + "compress": false, + "format": "plaintext", + "name": "python-sources", + "sources": [ + { + "listFile": "python-sources.txt", + }, + ], + "targetDirectory": ".", + }, + ], +} +`; + +exports[`createCompileRequest > createCompileRequest 6`] = ` +{ + "targets": [ + { + "compress": false, + "format": "plaintext", + "name": "python-sources", + "sources": [ + { + "listFile": "python-sources.txt", + }, + ], + "targetDirectory": "python", + }, + ], +} +`; + +exports[`createCompileRequest > createCompileRequest 7`] = ` +{ + "targets": [ + { + "compress": false, + "format": "plaintext", + "name": "python", + "sources": [ + { + "listFile": "python-sources.txt", + }, + ], + "targetDirectory": ".", + }, + ], +} +`; diff --git a/packages/cspell-tools/src/compiler/createCompileRequest.test.ts b/packages/cspell-tools/src/compiler/createCompileRequest.test.ts index 2c6ae91c8324..e4da45c47521 100644 --- a/packages/cspell-tools/src/compiler/createCompileRequest.test.ts +++ b/packages/cspell-tools/src/compiler/createCompileRequest.test.ts @@ -10,6 +10,9 @@ describe('createCompileRequest', () => { ${['src/words.txt']} | ${comp()} ${['src/words.txt', 'src/cities.txt']} | ${comp({ output: 'out' })} ${['src/words.txt', 'src/cities.txt']} | ${comp({ output: 'out', merge: 'combo' })} + ${[]} | ${comp({ listFile: ['python-sources.txt'] })} + ${[]} | ${comp({ listFile: ['python-sources.txt'], output: 'python' })} + ${[]} | ${comp({ listFile: ['python-sources.txt'], merge: 'python' })} `('createCompileRequest', ({ source, options }) => { const req = createCompileRequest(source, options); // Make sure the test passes on Windows. diff --git a/packages/cspell-tools/src/compiler/createCompileRequest.ts b/packages/cspell-tools/src/compiler/createCompileRequest.ts index 5fbee37c1ba3..eeab8e6d325e 100644 --- a/packages/cspell-tools/src/compiler/createCompileRequest.ts +++ b/packages/cspell-tools/src/compiler/createCompileRequest.ts @@ -1,11 +1,13 @@ import * as path from 'path'; import type { CompileCommonAppOptions } from '../AppOptions.js'; -import type { CompileRequest, DictionaryFormats, Target } from '../config/index.js'; +import type { CompileRequest, DictionaryFormats, DictionarySource, FileSource, Target } from '../config/index.js'; -export function createCompileRequest(sources: string[], options: CompileCommonAppOptions): CompileRequest { +export function createCompileRequest(sourceFiles: string[], options: CompileCommonAppOptions): CompileRequest { const { max_depth, maxDepth, experimental = [], split, keepRawCase, useLegacySplitter } = options; + const sources: DictionarySource[] = [...sourceFiles, ...(options.listFile || []).map((listFile) => ({ listFile }))]; + const targets = calcTargets(sources, options); const generateNonStrict = experimental.includes('compound') || undefined; @@ -23,7 +25,7 @@ export function createCompileRequest(sources: string[], options: CompileCommonAp return req; } -function calcTargets(sources: string[], options: CompileCommonAppOptions): Target[] { +function calcTargets(sources: DictionarySource[], options: CompileCommonAppOptions): Target[] { const { merge, output = '.' } = options; const format = calcFormat(options); @@ -42,7 +44,7 @@ function calcTargets(sources: string[], options: CompileCommonAppOptions): Targe } const targets: Target[] = sources.map((source) => { - const name = toTargetName(path.basename(source)); + const name = toTargetName(baseNameOfSource(source)); const target: Target = { name, targetDirectory: output, @@ -69,3 +71,11 @@ function parseNumber(s: string | undefined): number | undefined { const n = parseInt(s ?? ''); return isNaN(n) ? undefined : n; } + +function baseNameOfSource(source: DictionarySource): string { + return typeof source === 'string' ? source : isFileSource(source) ? source.filename : source.listFile; +} + +function isFileSource(source: DictionarySource): source is FileSource { + return typeof source !== 'string' && (source).filename !== undefined; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3b68bf52b2a0..ecd16c665c0d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,4 +1,4 @@ -lockfileVersion: '6.1' +lockfileVersion: '6.0' settings: autoInstallPeers: true @@ -696,6 +696,9 @@ importers: hunspell-reader: specifier: workspace:* version: link:../hunspell-reader + yaml: + specifier: ^2.3.1 + version: 2.3.1 devDependencies: '@types/glob': specifier: ^8.1.0