Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core: move to src, add TypeScript and rollup #2296

Merged
merged 27 commits into from
Feb 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
7a50c55
core: move to src, add TypeScript and rollup
Jan 28, 2025
4fd507b
core: move to src, add TypeScript and rollup
Jan 28, 2025
8c184e9
core: move to src, add TypeScript and rollup
Jan 28, 2025
0364394
core: move to src, add TypeScript and rollup
Jan 28, 2025
9d14f59
core: move to src, add TypeScript and rollup
Jan 28, 2025
12ddb22
add export @bugsnag/core/clone-client
Jan 29, 2025
92d4e9e
add jest custom resolver to handle packager.exports field
Jan 29, 2025
07cb860
copy jest dir into container
Jan 29, 2025
43995bc
fix test:types
Jan 29, 2025
2235492
remove jest-node-exports-resolver
Jan 29, 2025
076402b
Merge branch 'integration/typescript' into plat-12947
Jan 30, 2025
5765a17
add missing types package to test fixture
Jan 30, 2025
5c128f3
test: :adhesive_bandage: fix location when packing plugin-angular
gingerbenw Jan 30, 2025
1a68b71
add missing types package to test fixture
Jan 30, 2025
5249614
Merge branch 'bump-typescript' into plat-12947
Jan 31, 2025
3bd3332
update Dockerfile.ci eslint config location
Jan 31, 2025
cb227a1
Merge branch 'dash-13423-2' into plat-12947
Jan 31, 2025
470f373
fix imports
Jan 31, 2025
c46c43a
fix imports
Jan 31, 2025
bab00de
fix jest resolution
Feb 3, 2025
4bea0c0
Merge branch 'dash-13423-2' into plat-12947
Feb 3, 2025
13e28c4
Merge branch 'dash-13423-2' into plat-12947
Feb 3, 2025
7a842ec
add core to local-test-util install steps
Feb 3, 2025
63df48b
avoid ref to @bugsnag/core/event in generated types
Feb 3, 2025
a50c977
core: move tests from src to test
Feb 4, 2025
24f08b7
Merge branch 'integration/typescript' into plat-12947
Feb 11, 2025
ae50436
Merge pull request #2304 from bugsnag/plat-12947-3
djskinner Feb 11, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion bin/local-test-util
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ async function buildNotifiers (notifier) {
async function packNotifiers (notifier) {
const notifiers = notifier
? [ notifier ]
: [ 'js', 'browser', 'node', 'web-worker', 'plugin-angular', 'plugin-react', 'plugin-vue' ]
: [ 'core', 'js', 'browser', 'node', 'web-worker', 'plugin-angular', 'plugin-react', 'plugin-vue' ]
for (const n of notifiers) {
let packageLocation = `packages/${n}/`
if (n === 'plugin-angular') packageLocation += 'dist/'
Expand All @@ -138,9 +138,11 @@ async function installNotifiers (notifier) {
`--no-save`,
].concat(notifier
? [
`../../../../bugsnag-core-${require(`../packages/core/package.json`).version}.tgz`,
`../../../../bugsnag-${notifier}-${require(`../packages/${notifier}/package.json`).version}.tgz`
]
: [
`../../../../bugsnag-core-${require('../packages/core/package.json').version}.tgz`,
`../../../../bugsnag-browser-${require('../packages/browser/package.json').version}.tgz`,
`../../../../bugsnag-web-worker-${require('../packages/web-worker/package.json').version}.tgz`,
`../../../../bugsnag-plugin-react-${require('../packages/plugin-react/package.json').version}.tgz`,
Expand All @@ -158,6 +160,7 @@ async function installNgNotifier (notifier, version = '12') {
`install`,
`--no-package-lock`,
`--no-save`,
`../../../../../../bugsnag-core-${require('../packages/core/package.json').version}.tgz`,
`../../../../../../bugsnag-browser-${require('../packages/browser/package.json').version}.tgz`,
`../../../../../../bugsnag-js-${require('../packages/js/package.json').version}.tgz`,
`../../../../../../bugsnag-node-${require('../packages/node/package.json').version}.tgz`,
Expand Down
2 changes: 2 additions & 0 deletions config/electron-jest.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module.exports = {
projects: [
{
resolver: '<rootDir>/jest/node-exports-resolver',
setupFilesAfterEnv: ['<rootDir>/test/electron/setup.ts'],
clearMocks: true,
modulePathIgnorePatterns: ['.verdaccio', 'fixtures', 'examples'],
Expand All @@ -9,6 +10,7 @@ module.exports = {
testMatch: ['**/test/**/*.test-main.ts']
},
{
resolver: '<rootDir>/jest/node-exports-resolver',
setupFilesAfterEnv: ['<rootDir>/test/electron/setup.ts'],
clearMocks: true,
modulePathIgnorePatterns: ['.verdaccio', 'fixtures', 'examples'],
Expand Down
1 change: 1 addition & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const testsForPackage = (packageName) => `<rootDir>/packages/${packageName}/**/*.test.[jt]s?(x)`

const project = (displayName, packageNames, config = {}) => ({
resolver: '<rootDir>/jest/node-exports-resolver',
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the jest version being used doesn't support package.exports so we need to use a custom resolver. This is largely based on https://www.npmjs.com/package/jest-node-exports-resolver but required further changes:

An attempt to update jest was previously made (#2231) but there were issues because the helpers we use to run electron tests are no longer maintained and do not work with newwer versions of jest.

As alluded to in the previous PR its possible we could work around this by using multiple versions of jest (if a particular package needs a different version temporarily) rather than the single centralized way it is managed now.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally prefer the de-centralised approach to running the tests, but I think it was harder to track changes in test coverage? if we can get around that I'd be happy to take that approach in the future

roots: ['<rootDir>/packages'],
displayName,
testMatch: packageNames.map(testsForPackage),
Expand Down
183 changes: 183 additions & 0 deletions jest/node-exports-resolver.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
const fs = require('fs')
const path = require('path')
const packageUp = require('pkg-up')

const defaultConditions = ['require', 'node', 'default']

function findMainPackageJson (entryPath, packageName) {
entryPath = entryPath.replace(/\//g, path.sep)

let directoryName = path.dirname(entryPath)

let suspect = packageUp.sync({ cwd: directoryName })
if (fs.existsSync(suspect)) {
return JSON.parse(fs.readFileSync(suspect).toString())
}

while (directoryName && !directoryName.endsWith(packageName)) {
const parentDirectoryName = path.resolve(directoryName, '..')

if (parentDirectoryName === directoryName) break

directoryName = parentDirectoryName
}

suspect = path.resolve(directoryName, 'package.json')
if (fs.existsSync(suspect)) {
return JSON.parse(fs.readFileSync(suspect).toString())
}

return null
}

function getSelfReferencePath (packageName) {
let parentDirectoryName = __dirname
let directoryName

while (directoryName !== parentDirectoryName) {
directoryName = parentDirectoryName

try {
const { name } = require(path.resolve(directoryName, 'package.json'))

if (name === packageName) return directoryName
} catch {}

parentDirectoryName = path.resolve(directoryName, '..')
}
}

function getPackageJson (packageName) {
// Require `package.json` from the package, both from exported `exports` field
// in ESM packages, or directly from the file itself in CommonJS packages.
try {
return require(`${packageName}/package.json`)
} catch (requireError) {
if (requireError.code === 'MODULE_NOT_FOUND') {
throw requireError
}
if (requireError.code !== 'ERR_PACKAGE_PATH_NOT_EXPORTED') {
return console.error(
`Unexpected error while requiring ${packageName}:`, requireError
)
}
}

// modules's `package.json` does not provide the "./package.json" path at it's
// "exports" field. Get package level export or main field and try to resolve
// the package.json from it.
try {
const requestPath = require.resolve(packageName)

return requestPath && findMainPackageJson(requestPath, packageName)
} catch (resolveError) {
if (resolveError.code !== 'ERR_PACKAGE_PATH_NOT_EXPORTED') {
console.log(
`Unexpected error while performing require.resolve(${packageName}):`
)

return console.error(resolveError)
}
}

// modules's `package.json` does not provide a package level export nor main
// field. Try to find the package manually from `node_modules` folder.
const suspect = path.resolve(__dirname, '..', packageName, 'package.json')
if (fs.existsSync(suspect)) {
return JSON.parse(fs.readFileSync(suspect).toString())
}

console.warn(
'Could not retrieve package.json neither through require (package.json ' +
'itself is not within "exports" field), nor through require.resolve ' +
'(package.json does not specify "main" field) - falling back to default ' +
'resolver logic'
)
}

module.exports = (request, options) => {
const { conditions = defaultConditions, defaultResolver } = options

// NOTE: jest-sequencer is a special prefixed jest request
const isNodeModuleRequest =
!(
request.startsWith('.') ||
path.isAbsolute(request) ||
request.startsWith('jest-sequencer')
)

if (isNodeModuleRequest) {
const pkgPathParts = request.split('/')
const { length } = pkgPathParts

let packageName
let submoduleName

if (!request.startsWith('@')) {
packageName = pkgPathParts.shift()
submoduleName = length > 1 ? `./${pkgPathParts.join('/')}` : '.'
} else if (length >= 2) {
packageName = `${pkgPathParts.shift()}/${pkgPathParts.shift()}`
submoduleName = length > 2 ? `./${pkgPathParts.join('/')}` : '.'
}

if (packageName && submoduleName !== '.') {
const selfReferencePath = getSelfReferencePath(packageName)
if (selfReferencePath) packageName = selfReferencePath

const packageJson = getPackageJson(packageName)

if (!packageJson) {
console.error(`Failed to find package.json for ${packageName}`)
}

const { exports } = packageJson || {}
if (exports) {
let targetFilePath

if (typeof exports === 'string') { targetFilePath = exports } else if (Object.keys(exports).every((k) => k.startsWith('.'))) {
const [exportKey, exportValue] = Object.entries(exports)
.find(([k]) => {
if (k === submoduleName) return true
if (k.endsWith('*')) return submoduleName.startsWith(k.slice(0, -1))

return false
}) || []

if (typeof exportValue === 'string') {
targetFilePath = exportKey.endsWith('*')
? exportValue.replace(
/\*/, submoduleName.slice(exportKey.length - 1)
)
: exportValue
} else if (
conditions && exportValue != null && typeof exportValue === 'object'
) {
function resolveExport (exportValue, prevKeys) {
for (const [key, value] of Object.entries(exportValue)) {
// Duplicated nested conditions are undefined behaviour (and
// probably a format error or spec loop-hole), abort and
// delegate to Jest default resolver
if (prevKeys.includes(key)) return

if (!conditions.includes(key)) continue

if (typeof value === 'string') return value

return resolveExport(value, prevKeys.concat(key))
}
}

targetFilePath = resolveExport(exportValue, [])
}
}

if (targetFilePath) {
request = targetFilePath.replace('./', `${packageName}/`)
}
}
}
}

return defaultResolver(request, options)
}
Loading