@ampt/cli 1.0.16 → 1.0.18

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.
@@ -0,0 +1,267 @@
1
+ import fs from 'fs'
2
+ import Module from 'module'
3
+ import { dirname, extname, join, resolve as pathResolve } from 'path'
4
+ import { pathToFileURL, fileURLToPath } from 'url'
5
+ import JoyCon from 'joycon'
6
+ import { addHook } from 'pirates'
7
+ import sourceMapSupport from 'source-map-support'
8
+ import tsconfigLoader from 'tsconfig-paths/lib/tsconfig-loader'
9
+ import { createMatchPath } from 'tsconfig-paths'
10
+
11
+ const ESBUILD_PATH = process.env.ESBUILD_PATH || '/opt/nodejs/node_modules/esbuild'
12
+ const { default: esbuild } = await import(ESBUILD_PATH)
13
+ const { transform, transformSync } = esbuild
14
+
15
+ const baseURL = pathToFileURL(`${process.cwd()}/`).href
16
+
17
+ /*
18
+ TODO: consider caching loaded files in to memory if asked, so that we can cache them to negate the need to nft/esbuild?
19
+ at least cache transforms?
20
+ */
21
+
22
+ const joyCon = new JoyCon()
23
+ const matcherCache = {}
24
+
25
+ function isDir(path) {
26
+ const stats = fs.statSync(path, { throwIfNoEntry: false })
27
+ if (!stats) {
28
+ return false
29
+ }
30
+ return stats.isDirectory()
31
+ }
32
+
33
+ const bareImportSearchExtensions = ['.js', '.jsx', '.mjs', '.cjs', '.ts', '.tsx', '.mts', '.cts']
34
+
35
+ function bareImportSearch(specifier) {
36
+ for (const ext of [...bareImportSearchExtensions, ...bareImportSearchExtensions.map((ext) => `/index${ext}`)]) {
37
+ const fullPath = `${specifier}${ext}`
38
+ if (fs.existsSync(fullPath)) {
39
+ return fullPath
40
+ }
41
+ }
42
+ }
43
+
44
+ function matchPath(specifier, parentPath) {
45
+ // parentPath can be a file or a directory
46
+ const base = isDir(parentPath) ? parentPath : dirname(parentPath || process.cwd())
47
+
48
+ const configs = [joyCon.resolveSync(['tsconfig.json'], base), joyCon.resolveSync(['jsconfig.json'], base)]
49
+
50
+ for (const configPath of configs) {
51
+ if (configPath) {
52
+ let matcher = matcherCache[configPath]
53
+ if (!matcher) {
54
+ const loaded = tsconfigLoader.loadTsconfig(configPath)
55
+ if (loaded) {
56
+ const { baseUrl, paths } = loaded.compilerOptions ?? {}
57
+
58
+ if (paths) {
59
+ const absoluteBaseUrl = baseUrl ? pathResolve(dirname(configPath), baseUrl) : dirname(configPath)
60
+ matcher = createMatchPath(absoluteBaseUrl, paths)
61
+ matcherCache[configPath] = matcher
62
+ }
63
+ }
64
+ }
65
+
66
+ if (matcher) {
67
+ const matched = matcher(specifier)
68
+ if (matched) {
69
+ if (extensions.has(extname(matched))) {
70
+ return matched
71
+ }
72
+
73
+ const bareImportMatch = bareImportSearch(matched)
74
+ if (bareImportMatch) {
75
+ return bareImportMatch
76
+ }
77
+
78
+ return matched
79
+ }
80
+ }
81
+ }
82
+ }
83
+
84
+ if (isBareImport(specifier)) {
85
+ const bareImportMatch = bareImportSearch(join(base, specifier))
86
+ if (bareImportMatch) {
87
+ return bareImportMatch
88
+ }
89
+ }
90
+ }
91
+
92
+ const sourceMaps = {}
93
+
94
+ sourceMapSupport.install({
95
+ handleUncaughtExceptions: false,
96
+ environment: 'node',
97
+ retrieveSourceMap(file) {
98
+ if (sourceMaps[file]) {
99
+ return {
100
+ url: file,
101
+ map: sourceMaps[file]
102
+ }
103
+ } else {
104
+ const name = `${file}.map`
105
+ if (fs.existsSync(name)) {
106
+ const map = JSON.parse(fs.readFileSync(name).toString('utf8'))
107
+ sourceMaps[file] = map
108
+ return {
109
+ url: file,
110
+ map
111
+ }
112
+ }
113
+ }
114
+ return null
115
+ }
116
+ })
117
+
118
+ function isModulePackage(base) {
119
+ const { data = {} } = joyCon.loadSync(['package.json'], base)
120
+ return data.type === 'module'
121
+ }
122
+
123
+ const extensions = new Set(['.js', '.jsx', '.mjs', '.cjs', '.json', '.node', '.ts', '.tsx', '.mts', '.cts'])
124
+
125
+ function isBareImport(specifier) {
126
+ return (
127
+ (specifier.startsWith('./') ||
128
+ specifier.startsWith('..') ||
129
+ specifier.startsWith('/') ||
130
+ specifier.startsWith('file:///') ||
131
+ specifier.startsWith('file://.')) &&
132
+ !extensions.has(extname(specifier))
133
+ )
134
+ }
135
+
136
+ export function resolve(specifier, context, defaultResolve) {
137
+ if (specifier === '@serverless/cloud') {
138
+ return defaultResolve('@ampt/cloud', context)
139
+ }
140
+
141
+ if (!specifier.startsWith('file://') && !specifier.startsWith('node:')) {
142
+ const { parentURL = baseURL } = context
143
+ const matched = matchPath(specifier, fileURLToPath(parentURL))
144
+
145
+ if (matched) {
146
+ return { shortCircuit: true, url: pathToFileURL(matched).href }
147
+ }
148
+ }
149
+
150
+ return defaultResolve(specifier, context)
151
+ }
152
+
153
+ async function readTsConfig(forFilePath) {
154
+ const base = isDir(forFilePath) ? forFilePath : dirname(forFilePath || process.cwd())
155
+ for (const configPath of [joyCon.resolveSync(['tsconfig.json'], base), joyCon.resolveSync(['jsconfig.json'], base)]) {
156
+ if (fs.existsSync(configPath)) {
157
+ return fs.readFileSync(configPath).toString('utf8')
158
+ }
159
+ }
160
+ }
161
+
162
+ const transformOptions = {
163
+ '.jsx': {
164
+ loader: 'jsx',
165
+ tsconfig: true
166
+ },
167
+ '.ts': {
168
+ loader: 'ts',
169
+ tsconfig: true
170
+ },
171
+ '.tsx': {
172
+ loader: 'tsx',
173
+ tsconfig: true
174
+ },
175
+ '.cts': {
176
+ loader: 'ts',
177
+ esm: false
178
+ },
179
+ '.mts': {
180
+ loader: 'ts',
181
+ esm: true
182
+ }
183
+ }
184
+
185
+ export async function load(sourcefile, context, defaultLoad) {
186
+ const result = await defaultLoad(sourcefile, context)
187
+
188
+ if (sourcefile.startsWith('file://')) {
189
+ const path = fileURLToPath(sourcefile)
190
+ const ext = extname(path)
191
+ const options = transformOptions[ext]
192
+
193
+ if (options) {
194
+ const isESM =
195
+ context.format === 'module' ||
196
+ result.format === 'module' ||
197
+ options.esm === true ||
198
+ (options.esm === undefined && isModulePackage(path))
199
+
200
+ const tsconfigRaw = options.tsconfig ? await readTsConfig(path) : undefined
201
+
202
+ const { code, warnings, map } = await transform(result.source.toString(), {
203
+ sourcefile,
204
+ format: isESM ? 'esm' : 'cjs',
205
+ sourcemap: true,
206
+ sourcesContent: false,
207
+ loader: options.loader,
208
+ target: `node${process.version.slice(1)}`,
209
+ tsconfigRaw
210
+ })
211
+
212
+ sourceMaps[sourcefile] = map
213
+
214
+ if (warnings && warnings.length > 0) {
215
+ for (const warning of warnings) {
216
+ console.log(warning.location)
217
+ console.log(warning.text)
218
+ }
219
+ }
220
+
221
+ result.shortCircuit = true
222
+ result.source = code
223
+ result.format = isESM ? 'module' : 'commonjs'
224
+ }
225
+ }
226
+
227
+ return result
228
+ }
229
+
230
+ function compile(source, sourcefile) {
231
+ const {
232
+ code,
233
+ warnings = [],
234
+ map
235
+ } = transformSync(source, {
236
+ sourcefile,
237
+ format: 'cjs',
238
+ loader: 'ts',
239
+ sourcemap: true,
240
+ sourcesContent: false
241
+ })
242
+
243
+ sourceMaps[sourcefile] = map
244
+
245
+ for (const warning of warnings) {
246
+ console.warn(warning)
247
+ }
248
+
249
+ return code
250
+ }
251
+
252
+ addHook(compile, {
253
+ exts: ['.ts', '.cts'],
254
+ ignoreNodeModules: false
255
+ })
256
+
257
+ const originalResolveFilename = Module._resolveFilename
258
+ Module._resolveFilename = function (request, parent, ...rest) {
259
+ if (request === '@serverless/cloud') {
260
+ return originalResolveFilename.call(this, '@ampt/cloud', parent, ...rest)
261
+ }
262
+
263
+ // console.log('resolve filenae', request, parent)
264
+ // todo: why is parent undefined sometimes, and what can we pass to matchPath?
265
+ const matchedPath = parent ? matchPath(request, parent.path) : null
266
+ return originalResolveFilename.call(this, matchedPath || request, parent, ...rest)
267
+ }