-
Notifications
You must be signed in to change notification settings - Fork 12k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(@angular/build): set development/production condition
Ensures that we consistently set "development" for non-optimized and "production" for optimized builds. This is consistent with other bundlers (Vite/webpack/parcel/...).
- Loading branch information
Showing
6 changed files
with
257 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.dev/license | ||
*/ | ||
|
||
import { BuilderHarness } from './builder-harness'; | ||
|
||
export const GOOD_TARGET = './src/good.js'; | ||
export const BAD_TARGET = './src/bad.js'; | ||
|
||
/** Setup project for use of conditional imports. */ | ||
export async function setupConditionImport(harness: BuilderHarness<unknown>) { | ||
// Files that can be used as targets for the conditional import. | ||
await harness.writeFile('src/good.ts', `export const VALUE = 'good-value';`); | ||
await harness.writeFile('src/bad.ts', `export const VALUE = 'bad-value';`); | ||
|
||
// Simple application file that accesses conditional code. | ||
await harness.writeFile( | ||
'src/main.ts', | ||
`import {VALUE} from '#target'; | ||
console.log(VALUE); | ||
export default 42 as any; | ||
`, | ||
); | ||
|
||
// Ensure that good/bad can be resolved from tsconfig. | ||
const tsconfig = JSON.parse(harness.readFile('src/tsconfig.app.json')) as TypeScriptConfig; | ||
tsconfig.compilerOptions.moduleResolution = 'bundler'; | ||
tsconfig.files.push('good.ts', 'bad.ts'); | ||
await harness.writeFile('src/tsconfig.app.json', JSON.stringify(tsconfig)); | ||
} | ||
|
||
/** Update package.json with the given mapping for #target. */ | ||
export async function setTargetMapping(harness: BuilderHarness<unknown>, mapping: unknown) { | ||
await harness.writeFile( | ||
'package.json', | ||
JSON.stringify({ | ||
name: 'ng-test-app', | ||
imports: { | ||
'#target': mapping, | ||
}, | ||
}), | ||
); | ||
} | ||
|
||
interface TypeScriptConfig { | ||
compilerOptions: { | ||
moduleResolution: string; | ||
}; | ||
files: string[]; | ||
} |
95 changes: 95 additions & 0 deletions
95
packages/angular/build/src/builders/application/tests/behavior/build-conditions_spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.dev/license | ||
*/ | ||
|
||
import { | ||
setupConditionImport, | ||
setTargetMapping, | ||
} from '../../../../../../../../modules/testing/builder/src/dev_prod_mode'; | ||
import { buildApplication } from '../../index'; | ||
import { APPLICATION_BUILDER_INFO, BASE_OPTIONS, describeBuilder } from '../setup'; | ||
|
||
describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => { | ||
describe('Behavior: "conditional imports"', () => { | ||
beforeEach(async () => { | ||
await setupConditionImport(harness); | ||
}); | ||
|
||
interface ImportsTestCase { | ||
name: string; | ||
mapping: unknown; | ||
output?: string; | ||
} | ||
|
||
const GOOD_TARGET = './src/good.js'; | ||
const BAD_TARGET = './src/bad.js'; | ||
|
||
const testCases: ImportsTestCase[] = [ | ||
{ name: 'simple string', mapping: GOOD_TARGET }, | ||
{ | ||
name: 'default fallback without matching condition', | ||
mapping: { | ||
'never': BAD_TARGET, | ||
'default': GOOD_TARGET, | ||
}, | ||
}, | ||
{ | ||
name: 'development condition', | ||
mapping: { | ||
'development': BAD_TARGET, | ||
'default': GOOD_TARGET, | ||
}, | ||
}, | ||
{ | ||
name: 'production condition', | ||
mapping: { | ||
'production': GOOD_TARGET, | ||
'default': BAD_TARGET, | ||
}, | ||
}, | ||
{ | ||
name: 'browser condition (in browser)', | ||
mapping: { | ||
'browser': GOOD_TARGET, | ||
'default': BAD_TARGET, | ||
}, | ||
}, | ||
{ | ||
name: 'browser condition (in server)', | ||
output: 'server/main.server.mjs', | ||
mapping: { | ||
'browser': BAD_TARGET, | ||
'default': GOOD_TARGET, | ||
}, | ||
}, | ||
]; | ||
|
||
for (const testCase of testCases) { | ||
describe(testCase.name, () => { | ||
beforeEach(async () => { | ||
await setTargetMapping(harness, testCase.mapping); | ||
}); | ||
|
||
it('resolves to expected target', async () => { | ||
harness.useTarget('build', { | ||
...BASE_OPTIONS, | ||
optimization: true, | ||
ssr: true, | ||
server: 'src/main.ts', | ||
}); | ||
|
||
const { result } = await harness.executeOnce(); | ||
|
||
expect(result?.success).toBeTrue(); | ||
const outputFile = `dist/${testCase.output ?? 'browser/main.js'}`; | ||
harness.expectFile(outputFile).content.toContain('"good-value"'); | ||
harness.expectFile(outputFile).content.not.toContain('"bad-value"'); | ||
}); | ||
}); | ||
} | ||
}); | ||
}); |
100 changes: 100 additions & 0 deletions
100
packages/angular/build/src/builders/dev-server/tests/behavior/build-conditions_spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.dev/license | ||
*/ | ||
|
||
import { | ||
setupConditionImport, | ||
setTargetMapping, | ||
} from '../../../../../../../../modules/testing/builder/src/dev_prod_mode'; | ||
import { executeDevServer } from '../../index'; | ||
import { executeOnceAndFetch } from '../execute-fetch'; | ||
import { describeServeBuilder } from '../jasmine-helpers'; | ||
import { BASE_OPTIONS, DEV_SERVER_BUILDER_INFO } from '../setup'; | ||
|
||
describeServeBuilder( | ||
executeDevServer, | ||
DEV_SERVER_BUILDER_INFO, | ||
(harness, setupTarget, isApplicationBuilder) => { | ||
describe('Behavior: "conditional imports"', () => { | ||
if (!isApplicationBuilder) { | ||
it('requires esbuild', () => { | ||
expect(true).toBeTrue(); | ||
}); | ||
|
||
return; | ||
} | ||
|
||
beforeEach(async () => { | ||
setupTarget(harness); | ||
|
||
await setupConditionImport(harness); | ||
}); | ||
|
||
interface ImportsTestCase { | ||
name: string; | ||
mapping: unknown; | ||
output?: string; | ||
} | ||
|
||
const GOOD_TARGET = './src/good.js'; | ||
const BAD_TARGET = './src/bad.js'; | ||
|
||
const testCases: ImportsTestCase[] = [ | ||
{ name: 'simple string', mapping: GOOD_TARGET }, | ||
{ | ||
name: 'default fallback without matching condition', | ||
mapping: { | ||
'never': BAD_TARGET, | ||
'default': GOOD_TARGET, | ||
}, | ||
}, | ||
{ | ||
name: 'development condition', | ||
mapping: { | ||
'development': GOOD_TARGET, | ||
'default': BAD_TARGET, | ||
}, | ||
}, | ||
{ | ||
name: 'production condition', | ||
mapping: { | ||
'production': BAD_TARGET, | ||
'default': GOOD_TARGET, | ||
}, | ||
}, | ||
{ | ||
name: 'browser condition (in browser)', | ||
mapping: { | ||
'browser': GOOD_TARGET, | ||
'default': BAD_TARGET, | ||
}, | ||
}, | ||
]; | ||
|
||
for (const testCase of testCases) { | ||
describe(testCase.name, () => { | ||
beforeEach(async () => { | ||
await setTargetMapping(harness, testCase.mapping); | ||
}); | ||
|
||
it('resolves to expected target', async () => { | ||
harness.useTarget('serve', { | ||
...BASE_OPTIONS, | ||
}); | ||
|
||
const { result, response } = await executeOnceAndFetch(harness, '/main.js'); | ||
|
||
expect(result?.success).toBeTrue(); | ||
const output = await response?.text(); | ||
expect(output).toContain('good-value'); | ||
expect(output).not.toContain('bad-value'); | ||
}); | ||
}); | ||
} | ||
}); | ||
}, | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters