Skip to content

Commit 17ea62d

Browse files
committed
feat: add typescript and esm support
1 parent 3fca74f commit 17ea62d

19 files changed

+375
-188
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ tap-testdir*/
2929
!/SECURITY.md
3030
!/tap-snapshots/
3131
!/test/
32+
!/tsconfig.json
3233
!/workspace/
3334
/workspace/*
3435
!/workspace/test-workspace/

lib/config.js

+92-117
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
1-
const { relative, dirname, join, extname, posix, win32 } = require('path')
2-
const { defaults, pick, omit, uniq, isPlainObject } = require('lodash')
1+
const { relative, dirname, join, extname } = require('path')
2+
const { defaults, defaultsDeep, pick, omit, uniq, isPlainObject } = require('lodash')
33
const ciVersions = require('./util/ci-versions.js')
44
const parseDependabot = require('./util/dependabot.js')
55
const git = require('./util/git.js')
66
const gitignore = require('./util/gitignore.js')
77
const { mergeWithCustomizers, customizers } = require('./util/merge.js')
88
const { FILE_KEYS, parseConfig: parseFiles, getAddedFiles, mergeFiles } = require('./util/files.js')
9+
const template = require('./util/template.js')
10+
const getCmdPath = require('./util/get-cmd-path.js')
11+
const importOrRequire = require('./util/import-or-require.js')
12+
const { makePosix, deglob, posixDir, posixGlob } = require('./util/path.js')
13+
const { name: NAME, version: LATEST_VERSION } = require('../package.json')
914

1015
const CONFIG_KEY = 'templateOSS'
11-
const getPkgConfig = (pkg) => pkg[CONFIG_KEY] || {}
12-
13-
const { name: NAME, version: LATEST_VERSION } = require('../package.json')
1416
const MERGE_KEYS = [...FILE_KEYS, 'defaultContent', 'content']
1517
const DEFAULT_CONTENT = require.resolve(NAME)
18+
const getPkgConfig = (pkg) => pkg[CONFIG_KEY] || {}
1619

1720
const merge = mergeWithCustomizers(
1821
customizers.mergeArrays('branches', 'distPaths', 'allowPaths', 'ignorePaths'),
@@ -23,43 +26,6 @@ const merge = mergeWithCustomizers(
2326
}
2427
)
2528

26-
const makePosix = (v) => v.split(win32.sep).join(posix.sep)
27-
const deglob = (v) => makePosix(v).replace(/[/*]+$/, '')
28-
const posixDir = (v) => `${v === '.' ? '' : deglob(v).replace(/\/$/, '')}${posix.sep}`
29-
const posixGlob = (str) => `${posixDir(str)}**`
30-
31-
const getCmdPath = (key, { pkgConfig, rootConfig, isRoot, pkg, rootPkg }) => {
32-
const result = (local, isRelative) => {
33-
let root = local
34-
const isLocal = local.startsWith('.') || local.startsWith('/')
35-
36-
if (isLocal) {
37-
if (isRelative) {
38-
// Make a path relative from a workspace to the root if we are in a workspace
39-
local = makePosix(join(relative(pkg.path, rootPkg.path), local))
40-
}
41-
local = `node ${local}`
42-
root = `node ${root}`
43-
}
44-
45-
return {
46-
isLocal,
47-
local,
48-
root,
49-
}
50-
}
51-
52-
if (pkgConfig[key]) {
53-
return result(pkgConfig[key])
54-
}
55-
56-
if (rootConfig[key]) {
57-
return result(rootConfig[key], !isRoot)
58-
}
59-
60-
return result(key)
61-
}
62-
6329
const mergeConfigs = (...configs) => {
6430
const mergedConfig = merge(...configs.map(c => pick(c, MERGE_KEYS)))
6531
return defaults(mergedConfig, {
@@ -72,37 +38,33 @@ const mergeConfigs = (...configs) => {
7238
})
7339
}
7440

75-
const readContentPath = (path) => {
41+
const readContentPath = async (path) => {
7642
if (!path) {
7743
return {}
7844
}
7945

80-
let content = {}
8146
const index = extname(path) === '.js' ? path : join(path, 'index.js')
8247
const dir = dirname(index)
83-
84-
try {
85-
content = require(index)
86-
} catch {
87-
// its ok if this fails since the content dir
88-
// might only be to provide other files. the
89-
// index.js is optional
90-
}
48+
const content = await importOrRequire(index)
9149

9250
return { content, dir }
9351
}
9452

95-
const getConfig = (path, rawConfig) => {
96-
const config = omit(readContentPath(path).content, FILE_KEYS)
53+
const getConfig = async (path, rawConfig) => {
54+
const { content } = await readContentPath(path)
55+
const config = omit(content, FILE_KEYS)
9756
return merge(config, rawConfig ? omit(rawConfig, FILE_KEYS) : {})
9857
}
9958

100-
const getFiles = (path, rawConfig) => {
101-
const { content, dir } = readContentPath(path)
59+
const getFiles = async (path, rawConfig, templateSettings) => {
60+
const { content, dir } = await readContentPath(path)
10261
if (!dir) {
10362
return []
10463
}
105-
return [parseFiles(pick(content, FILE_KEYS), dir, pick(rawConfig, FILE_KEYS)), dir]
64+
return [
65+
parseFiles(pick(content, FILE_KEYS), dir, pick(rawConfig, FILE_KEYS), templateSettings),
66+
dir,
67+
]
10668
}
10769

10870
const getFullConfig = async ({
@@ -127,39 +89,15 @@ const getFullConfig = async ({
12789
// These config items are merged betweent the root and child workspaces and only come from
12890
// the package.json because they can be used to read configs from other the content directories
12991
const mergedConfig = mergeConfigs(rootPkg.config, pkg.config)
130-
131-
const defaultConfig = getConfig(DEFAULT_CONTENT)
132-
const [defaultFiles, defaultDir] = getFiles(DEFAULT_CONTENT, mergedConfig)
92+
const defaultConfig = await getConfig(DEFAULT_CONTENT)
13393
const useDefault = mergedConfig.defaultContent && defaultConfig
13494

135-
const rootConfig = getConfig(rootPkg.config.content, rootPkg.config)
136-
const [rootFiles, rootDir] = getFiles(rootPkg.config.content, mergedConfig)
95+
const rootConfig = await getConfig(rootPkg.config.content, rootPkg.config)
13796

13897
// The content config only gets set from the package we are in, it doesn't inherit
13998
// anything from the root
14099
const rootPkgConfig = merge(useDefault, rootConfig)
141-
const pkgConfig = merge(useDefault, getConfig(pkg.config.content, pkg.config))
142-
const [pkgFiles, pkgDir] = getFiles(mergedConfig.content, mergedConfig)
143-
144-
// Files get merged in from the default content (that template-oss provides) as well
145-
// as any content paths provided from the root or the workspace
146-
const fileDirs = uniq([useDefault && defaultDir, rootDir, pkgDir].filter(Boolean))
147-
const files = mergeFiles(useDefault && defaultFiles, rootFiles, pkgFiles)
148-
const repoFiles = isRoot ? files.rootRepo : files.workspaceRepo
149-
const moduleFiles = isRoot ? files.rootModule : files.workspaceModule
150-
151-
const allowRootDirs = [
152-
// Allways allow module files in root or workspaces
153-
...getAddedFiles(moduleFiles),
154-
...isRoot ? [
155-
// in the root allow all repo files
156-
...getAddedFiles(repoFiles),
157-
// and allow all workspace repo level files in the root
158-
...pkgs
159-
.filter(p => p.path !== rootPkg.path && p.config.workspaceRepo !== false)
160-
.flatMap(() => getAddedFiles(files.workspaceRepo)),
161-
] : [],
162-
]
100+
const pkgConfig = merge(useDefault, await getConfig(pkg.config.content, pkg.config))
163101

164102
const npmPath = getCmdPath('npm', { pkgConfig, rootConfig, isRoot, pkg, rootPkg })
165103
const npxPath = getCmdPath('npx', { pkgConfig, rootConfig, isRoot, pkg, rootPkg })
@@ -185,6 +123,8 @@ const getFullConfig = async ({
185123
? pkgConfig.releaseBranch.replace(/\*/g, pkgConfig.backport)
186124
: defaultBranch
187125

126+
const esm = pkg.pkgJson?.type === 'module' || !!pkgConfig.typescript || !!pkgConfig.esm
127+
188128
// all derived keys
189129
const derived = {
190130
isRoot,
@@ -209,25 +149,22 @@ const getFullConfig = async ({
209149
releaseBranch,
210150
publishTag,
211151
dependabot: parseDependabot(pkgConfig, defaultConfig, gitBranches.branches),
212-
// repo
152+
// paths
213153
repoDir: rootPkg.path,
214-
repoFiles,
215-
applyRepo: !!repoFiles,
216-
// module
217154
moduleDir: pkg.path,
218-
moduleFiles,
219-
applyModule: !!moduleFiles,
220-
// package
221155
pkgName: pkg.pkgJson.name,
222156
pkgNameFs: pkg.pkgJson.name.replace(/\//g, '-').replace(/@/g, ''),
223-
// paths
224157
pkgPath,
225158
pkgDir: posixDir(pkgPath),
226159
pkgGlob: posixGlob(pkgPath),
227160
pkgFlags: isWorkspace ? `-w ${pkg.pkgJson.name}` : '',
228161
allFlags: isMono ? '-ws -iwr --if-present' : '',
229162
workspacePaths,
230163
workspaceGlobs: workspacePaths.map(posixGlob),
164+
// type
165+
esm,
166+
cjsExt: esm ? 'cjs' : 'js',
167+
deleteJsExt: esm ? 'js' : 'cjs',
231168
// booleans to control application of updates
232169
isForce,
233170
isDogFood,
@@ -243,36 +180,29 @@ const getFullConfig = async ({
243180
lockfile: rootPkgConfig.lockfile,
244181
// ci versions / engines
245182
ciVersions: ciVersions.get(pkg.pkgJson.engines?.node, pkgConfig),
246-
// gitignore
247-
ignorePaths: [
248-
...gitignore.sort([
249-
...gitignore.allowRootDir(allowRootDirs),
250-
...isRoot && pkgConfig.lockfile ? ['!/package-lock.json'] : [],
251-
...(pkgConfig.allowPaths || []).map((p) => `!${p}`),
252-
...(pkgConfig.ignorePaths || []),
253-
]),
254-
// these cant be sorted since they rely on order
255-
// to allow a previously ignored directoy
256-
...isRoot
257-
? gitignore.allowDir(wsPkgs.map((p) => makePosix(relative(rootPkg.path, p.path))))
258-
: [],
259-
],
260183
// needs update if we are dogfooding this repo, with force argv, or its
261184
// behind the current version
262185
needsUpdate: isForce || isDogFood || !isLatest,
263186
// templateoss specific values
264187
__NAME__: NAME,
265188
__CONFIG_KEY__: CONFIG_KEY,
266189
__VERSION__: LATEST_VERSION,
267-
__PARTIAL_DIRS__: fileDirs,
268190
}
269191

270-
if (!pkgConfig.eslint) {
271-
derived.ignorePaths = derived.ignorePaths.filter(p => !p.includes('eslint'))
272-
if (Array.isArray(pkgConfig.requiredPackages?.devDependencies)) {
273-
pkgConfig.requiredPackages.devDependencies =
274-
pkgConfig.requiredPackages.devDependencies.filter(p => !p.includes('eslint'))
275-
}
192+
if (!pkgConfig.eslint && Array.isArray(pkgConfig.requiredPackages?.devDependencies)) {
193+
pkgConfig.requiredPackages.devDependencies =
194+
pkgConfig.requiredPackages.devDependencies.filter(p => !p.includes('eslint'))
195+
}
196+
197+
if (pkgConfig.typescript) {
198+
defaultsDeep(pkgConfig, { allowPaths: [], requiredPackages: { devDependencies: [] } })
199+
pkgConfig.distPaths = null
200+
pkgConfig.allowPaths.push('/src/')
201+
pkgConfig.requiredPackages.devDependencies.push(
202+
'typescript',
203+
'tshy',
204+
'@typescript-eslint/parser'
205+
)
276206
}
277207

278208
const gitUrl = await git.getUrl(rootPkg.path)
@@ -284,10 +214,55 @@ const getFullConfig = async ({
284214
}
285215
}
286216

287-
return {
288-
...pkgConfig,
289-
...derived,
290-
}
217+
const fullConfig = { ...pkgConfig, ...derived }
218+
219+
// files, come at the end since file names can be based on config
220+
const [defaultFiles, defaultDir] = await getFiles(DEFAULT_CONTENT, mergedConfig, fullConfig)
221+
const [rootFiles, rootDir] = await getFiles(rootPkg.config.content, mergedConfig, fullConfig)
222+
const [pkgFiles, pkgDir] = await getFiles(mergedConfig.content, mergedConfig, fullConfig)
223+
224+
// Files get merged in from the default content (that template-oss provides) as well
225+
// as any content paths provided from the root or the workspace
226+
const fileDirs = uniq([useDefault && defaultDir, rootDir, pkgDir].filter(Boolean))
227+
const files = mergeFiles(useDefault && defaultFiles, rootFiles, pkgFiles)
228+
const repoFiles = isRoot ? files.rootRepo : files.workspaceRepo
229+
const moduleFiles = isRoot ? files.rootModule : files.workspaceModule
230+
231+
Object.assign(fullConfig, {
232+
repoFiles,
233+
moduleFiles,
234+
applyRepo: !!repoFiles,
235+
applyModule: !!moduleFiles,
236+
__PARTIAL_DIRS__: fileDirs,
237+
// gitignore, these use the full config so need to come at the very end
238+
ignorePaths: [
239+
...gitignore.sort([
240+
...gitignore.allowRootDir([
241+
// Allways allow module files in root or workspaces
242+
...getAddedFiles(moduleFiles).map(s => template(s, fullConfig)),
243+
...isRoot ? [
244+
// in the root allow all repo files
245+
...getAddedFiles(repoFiles).map(s => template(s, fullConfig)),
246+
// and allow all workspace repo level files in the root
247+
...pkgs
248+
.filter(p => p.path !== rootPkg.path && p.config.workspaceRepo !== false)
249+
.flatMap(() => getAddedFiles(files.workspaceRepo)),
250+
] : [],
251+
]),
252+
...isRoot && pkgConfig.lockfile ? ['!/package-lock.json'] : [],
253+
...(pkgConfig.allowPaths || []).map((p) => `!${p}`),
254+
...(pkgConfig.distPaths || []).map((p) => `!/${p}`),
255+
...(pkgConfig.ignorePaths || []),
256+
]),
257+
// these cant be sorted since they rely on order
258+
// to allow a previously ignored directoy
259+
...isRoot
260+
? gitignore.allowDir(wsPkgs.map((p) => makePosix(relative(rootPkg.path, p.path))))
261+
: [],
262+
].filter(p => !pkgConfig.eslint ? !p.includes('eslint') : true),
263+
})
264+
265+
return fullConfig
291266
}
292267

293268
module.exports = getFullConfig

lib/content/eslintrc-js.hbs

+11
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,18 @@ module.exports = {
1313
{{#each workspaceGlobs}}
1414
'{{ . }}',
1515
{{/each}}
16+
{{#if typescript}}
17+
'dist/',
18+
{{/if}}
1619
],
20+
{{#if typescript}}
21+
parser: '@typescript-eslint/parser',
22+
settings: {
23+
'import/resolver': {
24+
typescript: {},
25+
},
26+
},
27+
{{/if}}
1728
extends: [
1829
'@npmcli',
1930
...localConfigs,

0 commit comments

Comments
 (0)