Skip to content

Commit

Permalink
refactor: ease tree shaking (#93)
Browse files Browse the repository at this point in the history
the previous code was not easy to tree-shake.
this caused a much too large build result, which included unused
chunks...

lets ease the tree shaking 

-----

old:
```
➤ YN0000: ? Bundle size: 778.13 KiB
```

new: 
```
➤ YN0000: ? Bundle size: 574.05 KiB
```

Signed-off-by: Jan Kowalleck <jan.kowalleck@gmail.com>
  • Loading branch information
jkowalleck committed May 15, 2024
1 parent f45b872 commit 128de7e
Show file tree
Hide file tree
Showing 20 changed files with 97 additions and 63 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@


# Only used during production build.
/src/buildtime*
/src/__buildtime*


# Everything below here is from https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
}
],
"dependencies": {
"@cyclonedx/cyclonedx-library": "^6.6.0",
"@cyclonedx/cyclonedx-library": "^6.8.0",
"@yarnpkg/cli": "^4.1.0",
"@yarnpkg/core": "^4.0.3",
"@yarnpkg/fslib": "^3.0.2",
Expand All @@ -76,6 +76,7 @@
"eslint-plugin-n": "16.6.2",
"eslint-plugin-promise": "6.1.1",
"eslint-plugin-simple-import-sort": "12.0.0",
"fast-glob": "^3.3.2",
"mocha": "10.4.0",
"npm-run-all2": "^6.1.2",
"rimraf": "^5.0.5",
Expand Down
27 changes: 27 additions & 0 deletions src/_buildtimeInfo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*!
This file is part of CycloneDX SBOM plugin for yarn.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
SPDX-License-Identifier: Apache-2.0
Copyright (c) OWASP Foundation. All Rights Reserved.
*/

/* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
export async function getBuildtimeInfo () {
/*
The included file is generated by `../tools/gather-buildtime-info.cjs`.
Its content is pseudo-dynamic and so is the return type of this function.
*/
return (await import('./__buildtimeInfo.json')).default
}
33 changes: 19 additions & 14 deletions src/builders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,18 @@ SPDX-License-Identifier: Apache-2.0
Copyright (c) OWASP Foundation. All Rights Reserved.
*/

import { type Builders, Enums, type Factories, Models, Utils } from '@cyclonedx/cyclonedx-library'
import type { Builders, Factories } from '@cyclonedx/cyclonedx-library'
// import sub-modules so to prevent load of unused not-tree-shakable dependencies - like 'AJV'
import { ComponentType, LicenseAcknowledgement } from '@cyclonedx/cyclonedx-library/Enums'
import * as Models from '@cyclonedx/cyclonedx-library/Models'
import { Component, Property } from '@cyclonedx/cyclonedx-library/Models'
import { BomUtility } from '@cyclonedx/cyclonedx-library/Utils'
import { Cache, type FetchOptions, type Locator, type LocatorHash, type Package, type Project, structUtils, ThrowReport, type Workspace } from '@yarnpkg/core'
import { ppath } from '@yarnpkg/fslib'
import normalizePackageData from 'normalize-package-data'
import type { PackageURL } from 'packageurl-js'

import { getBuildtimeInfo } from './_buildtimeInfo'
import { isString } from './_helpers'
import { PropertyNames, PropertyValueBool } from './properties'

Expand All @@ -38,10 +44,10 @@ interface BomBuilderOptions {
export class BomBuilder {
toolBuilder: Builders.FromNodePackageJson.ToolBuilder
componentBuilder: Builders.FromNodePackageJson.ComponentBuilder
purlFactory: Factories.FromNodePackageJson.PackageUrlFactory
purlFactory: Factories.PackageUrlFactory

omitDevDependencies: boolean
metaComponentType: Enums.ComponentType
metaComponentType: ComponentType
reproducible: boolean
shortPURLs: boolean

Expand All @@ -59,7 +65,7 @@ export class BomBuilder {
this.purlFactory = purlFactory

this.omitDevDependencies = options.omitDevDependencies ?? false
this.metaComponentType = options.metaComponentType ?? Enums.ComponentType.Application
this.metaComponentType = options.metaComponentType ?? ComponentType.Application
this.reproducible = options.reproducible ?? false
this.shortPURLs = options.shortPURLs ?? false

Expand All @@ -71,7 +77,7 @@ export class BomBuilder {
const fetchManifest: ManifestFetcher = await this.makeManifestFetcher(workspace.project)

const setLicensesDeclared = function (license: Models.License): void {
license.acknowledgement = Enums.LicenseAcknowledgement.Declared
license.acknowledgement = LicenseAcknowledgement.Declared
}

/* eslint-disable-next-line @typescript-eslint/strict-boolean-expressions, @typescript-eslint/prefer-nullish-coalescing --
Expand All @@ -92,10 +98,10 @@ export class BomBuilder {

if (this.reproducible) {
bom.metadata.properties.add(
new Models.Property(PropertyNames.Reproducible, PropertyValueBool.True)
new Property(PropertyNames.Reproducible, PropertyValueBool.True)
)
} else {
bom.serialNumber = Utils.BomUtility.randomSerialNumber()
bom.serialNumber = BomUtility.randomSerialNumber()
bom.metadata.timestamp = new Date()
}

Expand Down Expand Up @@ -128,7 +134,7 @@ export class BomBuilder {
return bom
}

private makeComponentFromWorkspace (workspace: Workspace, type?: Enums.ComponentType | undefined): Models.Component | false | undefined {
private makeComponentFromWorkspace (workspace: Workspace, type?: ComponentType | undefined): Models.Component | false | undefined {
return this.makeComponent(workspace.anchoredLocator, workspace.manifest.raw, type)
}

Expand All @@ -152,7 +158,7 @@ export class BomBuilder {
private async makeComponentFromPackage (
pkg: Package,
fetchManifest: ManifestFetcher,
type?: Enums.ComponentType | undefined
type?: ComponentType | undefined
): Promise<Models.Component | false | undefined> {
const data = await fetchManifest(pkg)
// the data in the manifest might be incomplete, so lets set the properties that yarn discovered and fixed
Expand All @@ -162,7 +168,7 @@ export class BomBuilder {
return this.makeComponent(pkg, data, type)
}

private makeComponent (locator: Locator, data: any, type?: Enums.ComponentType | undefined): Models.Component | false | undefined {
private makeComponent (locator: Locator, data: any, type?: ComponentType | undefined): Models.Component | false | undefined {
// work with a deep copy, because `normalizePackageData()` might modify the data
const dataC = structuredClonePolyfill(data)
normalizePackageData(dataC as normalizePackageData.Input)
Expand Down Expand Up @@ -204,8 +210,7 @@ export class BomBuilder {
}

private async * makeTools (): AsyncGenerator<Models.Tool> {
const { default: buildtimeInfo } = await import('./buildtimeInfo.json')
for (const nfo of Object.values(buildtimeInfo)) {
for (const nfo of Object.values(await getBuildtimeInfo())) {
const tool = this.toolBuilder.makeTool(nfo)
if (tool !== undefined) {
yield tool
Expand Down Expand Up @@ -249,7 +254,7 @@ export class BomBuilder {
continue // for-loop
}
if (_depC === undefined) {
depComponent = new DummyComponent(Enums.ComponentType.Library, `InterferedDependency.${_depIDN}`)
depComponent = new DummyComponent(ComponentType.Library, `InterferedDependency.${_depIDN}`)
this.console.warn('WARN | InterferedDependency %j', _depIDN)
} else {
depComponent = _depC
Expand All @@ -265,7 +270,7 @@ export class BomBuilder {
}
}

class DummyComponent extends Models.Component {
class DummyComponent extends Component {
constructor (type: Models.Component['type'], name: Models.Component['name']) {
super(type, `DummyComponent.${name}`, {
bomRef: `DummyComponent.${name}`,
Expand Down
31 changes: 18 additions & 13 deletions src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@ SPDX-License-Identifier: Apache-2.0
Copyright (c) OWASP Foundation. All Rights Reserved.
*/

import { Builders, Enums, Factories, Serialize, Spec } from '@cyclonedx/cyclonedx-library'
// import sub-modules so to prevent load of unused not-tree-shakable dependencies - like 'AJV'
import { FromNodePackageJson as PJB } from '@cyclonedx/cyclonedx-library/Builders'
import { ComponentType } from '@cyclonedx/cyclonedx-library/Enums'
import { FromNodePackageJson as PJF, LicenseFactory, PackageUrlFactory } from '@cyclonedx/cyclonedx-library/Factories'
import * as Serialize from '@cyclonedx/cyclonedx-library/Serialize'
import { SpecVersionDict, Version as SpecVersion } from '@cyclonedx/cyclonedx-library/Spec'
import { type CommandContext, Configuration, Project } from '@yarnpkg/core'
import { Command, Option } from 'clipanion'
import { openSync } from 'fs'
Expand Down Expand Up @@ -60,10 +65,10 @@ export class MakeSbomCommand extends Command<CommandContext> {
details: 'Recursively scan workspace dependencies and emits them as Software-Bill-of-Materials(SBOM) in CycloneDX format.'
})

specVersion = makeChoiceSwitch<Spec.Version>(
specVersion = makeChoiceSwitch<SpecVersion>(
'--spec-version',
Object.keys(Spec.SpecVersionDict).sort(),
Spec.Version.v1dot5,
Object.keys(SpecVersionDict).sort(),
SpecVersion.v1dot5,
'Which version of CycloneDX to use.'
)

Expand All @@ -90,10 +95,10 @@ export class MakeSbomCommand extends Command<CommandContext> {
'(default: true if the NODE_ENV environment variable is set to "production", otherwise false)'
})

mcType = makeChoiceSwitch<Enums.ComponentType>(
mcType = makeChoiceSwitch<ComponentType>(
'--mc-type',
[Enums.ComponentType.Application, Enums.ComponentType.Library, Enums.ComponentType.Firmware],
Enums.ComponentType.Application,
[ComponentType.Application, ComponentType.Library, ComponentType.Firmware],
ComponentType.Application,
'Type of the main component.'
)

Expand Down Expand Up @@ -146,16 +151,16 @@ export class MakeSbomCommand extends Command<CommandContext> {
myConsole.debug('DEBUG | workspace:', workspace.cwd)
await workspace.project.restoreInstallState()

const extRefFactory = new Factories.FromNodePackageJson.ExternalReferenceFactory()
const extRefFactory = new PJF.ExternalReferenceFactory()

myConsole.log('LOG | gathering BOM data ...')
const bom = await (new BomBuilder(
new Builders.FromNodePackageJson.ToolBuilder(extRefFactory),
new Builders.FromNodePackageJson.ComponentBuilder(
new PJB.ToolBuilder(extRefFactory),
new PJB.ComponentBuilder(
extRefFactory,
new Factories.LicenseFactory()
new LicenseFactory()
),
new Factories.FromNodePackageJson.PackageUrlFactory('npm'),
new PackageUrlFactory('npm'),
{
omitDevDependencies: this.production,
metaComponentType: this.mcType,
Expand All @@ -165,7 +170,7 @@ export class MakeSbomCommand extends Command<CommandContext> {
myConsole
)).buildFromWorkspace(workspace)

const spec = Spec.SpecVersionDict[this.specVersion]
const spec = SpecVersionDict[this.specVersion]
if (undefined === spec) {
throw new Error('unsupported spec-version')
}
Expand Down
3 changes: 2 additions & 1 deletion src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Copyright (c) OWASP Foundation. All Rights Reserved.
import { BaseCommand } from '@yarnpkg/cli'
import type { Plugin } from '@yarnpkg/core'

import { getBuildtimeInfo } from './_buildtimeInfo'
import { MakeSbomCommand } from './commands'

class CyclonedxCommand extends MakeSbomCommand {
Expand All @@ -34,7 +35,7 @@ class CyclonedxVersionCommand extends BaseCommand {
static override readonly paths = CyclonedxCommand.paths.map(p => [...p, '--version'])

async execute (): Promise<void> {
const { self: { name, version } } = await import('./buildtimeInfo.json')
const { self: { name, version } } = await getBuildtimeInfo()
this.context.stdout.write(`${name} v${version}\n`)
}
}
Expand Down
2 changes: 1 addition & 1 deletion tests/_data/snapshots/plain_bundled-dependencies.json.bin

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion tests/_data/snapshots/plain_concurrent-versions.json.bin

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion tests/_data/snapshots/plain_dev-dependencies.json.bin

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions tests/_data/snapshots/plain_git-protocol-dependency.json.bin

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions tests/_data/snapshots/plain_juice-shop.json.bin

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions tests/_data/snapshots/plain_local-dependencies.json.bin

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 128de7e

Please sign in to comment.