Skip to content

Commit

Permalink
feat(@schematics/angular): replace assets with public directory
Browse files Browse the repository at this point in the history
The `assets` directory is confusing for the users and commonly users place "assets" which are not meant to be copied but instead processed by the build system. This causes some files both bundled and copied.

With this change we rename the `assets` directory to `public` and also move the `favicon.ico` inside this newly created directory.
  • Loading branch information
alan-agius4 committed Feb 14, 2024
1 parent 0f84ae9 commit ab8bac4
Show file tree
Hide file tree
Showing 24 changed files with 64 additions and 68 deletions.
2 changes: 1 addition & 1 deletion goldens/public-api/angular_devkit/build_angular/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export interface AssetPatternObject {
glob: string;
ignore?: string[];
input: string;
output: string;
output?: string;
}

// @public
Expand Down
6 changes: 5 additions & 1 deletion packages/angular/pwa/pwa/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,11 +146,15 @@ export default function (options: PwaOptions): Rule {
const { title, ...swOptions } = options;

await writeWorkspace(host, workspace);
let assetsDir = posix.join(sourcePath, 'assets');
if (!host.exists(assetsDir)) {
assetsDir = posix.join(sourcePath, 'public');
}

return chain([
externalSchematic('@schematics/angular', 'service-worker', swOptions),
mergeWith(apply(url('./files/root'), [template({ ...options }), move(sourcePath)])),
mergeWith(apply(url('./files/assets'), [move(posix.join(sourcePath, 'assets'))])),
mergeWith(apply(url('./files/assets'), [move(assetsDir)])),
...[...indexFiles].map((path) => updateIndexFile(path)),
]);
};
Expand Down
2 changes: 1 addition & 1 deletion packages/angular/pwa/pwa/index_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ describe('PWA Schematic', () => {

it('should create icon files', async () => {
const dimensions = [72, 96, 128, 144, 152, 192, 384, 512];
const iconPath = '/projects/bar/src/assets/icons/icon-';
const iconPath = '/projects/bar/src/public/icons/icon-';
const tree = await schematicRunner.runSchematic('ng-add', defaultOptions, appTree);

dimensions.forEach((d) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -556,11 +556,12 @@
},
"output": {
"type": "string",
"default": "",
"description": "Absolute path within the output."
}
},
"additionalProperties": false,
"required": ["glob", "input", "output"]
"required": ["glob", "input"]
},
{
"type": "string"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -467,11 +467,12 @@
},
"output": {
"type": "string",
"default": "",
"description": "Absolute path within the output."
}
},
"additionalProperties": false,
"required": ["glob", "input", "output"]
"required": ["glob", "input"]
},
{
"type": "string"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -455,11 +455,12 @@
},
"output": {
"type": "string",
"default": "",
"description": "Absolute path within the output."
}
},
"additionalProperties": false,
"required": ["glob", "input", "output"]
"required": ["glob", "input"]
},
{
"type": "string"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@
},
"output": {
"type": "string",
"default": "",
"description": "Absolute path within the output."
},
"ignore": {
Expand All @@ -301,7 +302,7 @@
}
},
"additionalProperties": false,
"required": ["glob", "input", "output"]
"required": ["glob", "input"]
},
{
"type": "string"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,11 +252,12 @@
},
"output": {
"type": "string",
"default": "",
"description": "Absolute path within the output."
}
},
"additionalProperties": false,
"required": ["glob", "input", "output"]
"required": ["glob", "input"]
},
{
"type": "string"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@
},
"output": {
"type": "string",
"default": "",
"description": "Absolute path within the output."
},
"ignore": {
Expand All @@ -280,7 +281,7 @@
}
},
"additionalProperties": false,
"required": ["glob", "input", "output"]
"required": ["glob", "input"]
},
{
"type": "string"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ export function assetPatterns(root: string, assets: AssetPatternClass[]) {
return assets.map((asset: AssetPatternClass, index: number): ObjectPattern => {
// Resolve input paths relative to workspace root and add slash at the end.
// eslint-disable-next-line prefer-const
let { input, output, ignore = [], glob } = asset;
let { input, output = '', ignore = [], glob } = asset;
input = path.resolve(root, input).replace(/\\/g, '/');
input = input.endsWith('/') ? input : input + '/';
output = output.endsWith('/') ? output : output + '/';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import { BaseException } from '@angular-devkit/core';
import { statSync } from 'fs';
import assert from 'node:assert';
import * as path from 'path';
import { AssetPattern, AssetPatternClass } from '../builders/browser/schema';

Expand All @@ -22,7 +23,7 @@ export function normalizeAssetPatterns(
workspaceRoot: string,
projectRoot: string,
projectSourceRoot: string | undefined,
): AssetPatternClass[] {
): (AssetPatternClass & { output: string })[] {
if (assetPatterns.length === 0) {
return [];
}
Expand Down Expand Up @@ -68,13 +69,15 @@ export function normalizeAssetPatterns(

assetPattern = { glob, input, output };
} else {
assetPattern.output = path.join('.', assetPattern.output);
assetPattern.output = path.join('.', assetPattern.output ?? '');
}

assert(assetPattern.output !== undefined);

if (assetPattern.output.startsWith('..')) {
throw new Error('An asset cannot be written to a location outside of the output path.');
}

return assetPattern;
return assetPattern as AssetPatternClass & { output: string };
});
}
Empty file.
2 changes: 1 addition & 1 deletion packages/schematics/angular/application/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ function addAppToWorkspaceFile(
polyfills: ['zone.js'],
tsConfig: `${projectRoot}tsconfig.app.json`,
inlineStyleLanguage,
assets: [`${sourceRoot}/favicon.ico`, `${sourceRoot}/assets`],
assets: [{ 'glob': '**/*', 'input': `${sourceRoot}/public` }],
styles: [`${sourceRoot}/styles.${options.style}`],
scripts: [],
},
Expand Down
12 changes: 6 additions & 6 deletions packages/schematics/angular/application/index_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ describe('Application Schematic', () => {
jasmine.arrayContaining([
'/projects/foo/tsconfig.app.json',
'/projects/foo/tsconfig.spec.json',
'/projects/foo/src/favicon.ico',
'/projects/foo/src/public/favicon.ico',
'/projects/foo/src/index.html',
'/projects/foo/src/main.ts',
'/projects/foo/src/styles.css',
Expand Down Expand Up @@ -263,7 +263,7 @@ describe('Application Schematic', () => {
jasmine.arrayContaining([
'/tsconfig.app.json',
'/tsconfig.spec.json',
'/src/favicon.ico',
'/src/public/favicon.ico',
'/src/index.html',
'/src/main.ts',
'/src/styles.css',
Expand Down Expand Up @@ -448,7 +448,7 @@ describe('Application Schematic', () => {
expect(files).toEqual(
jasmine.arrayContaining([
'/projects/foo/tsconfig.app.json',
'/projects/foo/src/favicon.ico',
'/projects/foo/src/public/favicon.ico',
'/projects/foo/src/index.html',
'/projects/foo/src/main.ts',
'/projects/foo/src/styles.css',
Expand All @@ -473,7 +473,7 @@ describe('Application Schematic', () => {
expect(files).toEqual(
jasmine.arrayContaining([
'/projects/foo/tsconfig.app.json',
'/projects/foo/src/favicon.ico',
'/projects/foo/src/public/favicon.ico',
'/projects/foo/src/index.html',
'/projects/foo/src/main.ts',
'/projects/foo/src/styles.css',
Expand All @@ -499,7 +499,7 @@ describe('Application Schematic', () => {
expect(files).toEqual(
jasmine.arrayContaining([
'/projects/foo/tsconfig.app.json',
'/projects/foo/src/favicon.ico',
'/projects/foo/src/public/favicon.ico',
'/projects/foo/src/index.html',
'/projects/foo/src/main.ts',
'/projects/foo/src/styles.css',
Expand All @@ -519,7 +519,7 @@ describe('Application Schematic', () => {
jasmine.arrayContaining([
'/projects/foo/tsconfig.app.json',
'/projects/foo/tsconfig.spec.json',
'/projects/foo/src/favicon.ico',
'/projects/foo/src/public/favicon.ico',
'/projects/foo/src/index.html',
'/projects/foo/src/main.ts',
'/projects/foo/src/styles.css',
Expand Down
22 changes: 11 additions & 11 deletions tests/legacy-cli/e2e/tests/build/assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,21 @@ import { updateJsonFile } from '../../utils/project';
import { expectToFail } from '../../utils/utils';

export default async function () {
await writeFile('src/assets/.file', '');
await writeFile('src/assets/test.abc', 'hello world');
await writeFile('src/public/.file', '');
await writeFile('src/public/test.abc', 'hello world');

await ng('build', '--configuration=development');

await expectFileToExist('dist/test-project/browser/favicon.ico');
await expectFileToExist('dist/test-project/browser/assets/.file');
await expectFileToMatch('dist/test-project/browser/assets/test.abc', 'hello world');
await expectToFail(() => expectFileToExist('dist/test-project/browser/assets/.gitkeep'));
await expectFileToExist('dist/test-project/browser/.file');
await expectFileToMatch('dist/test-project/browser/test.abc', 'hello world');
await expectToFail(() => expectFileToExist('dist/test-project/browser/.gitkeep'));

// Ensure `followSymlinks` option follows symlinks
await updateJsonFile('angular.json', (workspaceJson) => {
const appArchitect = workspaceJson.projects['test-project'].architect;
appArchitect['build'].options.assets = [
{ glob: '**/*', input: 'src/assets', output: 'assets', followSymlinks: true },
{ glob: '**/*', input: 'src/public', followSymlinks: true },
];
});
fs.mkdirSync('dirToSymlink/subdir1', { recursive: true });
Expand All @@ -28,12 +28,12 @@ export default async function () {
fs.writeFileSync('dirToSymlink/subdir1/b.txt', '');
fs.writeFileSync('dirToSymlink/subdir2/c.txt', '');
fs.writeFileSync('dirToSymlink/subdir2/subsubdir1/d.txt', '');
fs.symlinkSync(process.cwd() + '/dirToSymlink', 'src/assets/symlinkDir');
fs.symlinkSync(process.cwd() + '/dirToSymlink', 'src/public/symlinkDir');

await ng('build', '--configuration=development');

await expectFileToExist('dist/test-project/browser/assets/symlinkDir/a.txt');
await expectFileToExist('dist/test-project/browser/assets/symlinkDir/subdir1/b.txt');
await expectFileToExist('dist/test-project/browser/assets/symlinkDir/subdir2/c.txt');
await expectFileToExist('dist/test-project/browser/assets/symlinkDir/subdir2/subsubdir1/d.txt');
await expectFileToExist('dist/test-project/browser/symlinkDir/a.txt');
await expectFileToExist('dist/test-project/browser/symlinkDir/subdir1/b.txt');
await expectFileToExist('dist/test-project/browser/symlinkDir/subdir2/c.txt');
await expectFileToExist('dist/test-project/browser/symlinkDir/subdir2/subsubdir1/d.txt');
}
7 changes: 5 additions & 2 deletions tests/legacy-cli/e2e/tests/build/css-urls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
import { copyProjectAsset } from '../../utils/assets';
import { expectToFail } from '../../utils/utils';
import { getGlobalVariable } from '../../utils/env';
import { mkdir } from 'node:fs/promises';

const imgSvg = `
<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
Expand All @@ -22,6 +23,8 @@ export default async function () {
? './dist/test-project/browser'
: './dist/test-project/browser/media';

await mkdir('src/public/assets/', { recursive: true });

await Promise.resolve()
// Verify absolute/relative paths in global/component css.
.then(() =>
Expand All @@ -34,8 +37,8 @@ export default async function () {
h3 { background: url('/assets/component-img-absolute.svg'); }
h4 { background: url('../assets/component-img-relative.png'); }
`,
'src/assets/global-img-absolute.svg': imgSvg,
'src/assets/component-img-absolute.svg': imgSvg,
'src/public/assets/global-img-absolute.svg': imgSvg,
'src/public/assets/component-img-absolute.svg': imgSvg,
}),
)
.then(() => copyProjectAsset('images/spectrum.png', './src/assets/global-img-relative.png'))
Expand Down
1 change: 0 additions & 1 deletion tests/legacy-cli/e2e/tests/build/multiple-configs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ export default async function () {
sourceMap: true,
outputHashing: 'none',
vendorChunk: true,
assets: ['src/favicon.ico', 'src/assets'],
styles: ['src/styles.css'],
scripts: [],
budgets: [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export default async function () {
};
`,
// Add asset
'src/assets/media.json': JSON.stringify({ dataFromAssets: true }),
'src/public/media.json': JSON.stringify({ dataFromAssets: true }),
// Update component to do an HTTP call to asset.
'src/app/app.component.ts': `
import { Component, inject } from '@angular/core';
Expand All @@ -60,7 +60,7 @@ export default async function () {
data: any;
constructor() {
const http = inject(HttpClient);
http.get('/assets/media.json').subscribe((d) => {
http.get('/media.json').subscribe((d) => {
this.data = d;
});
}
Expand Down
19 changes: 0 additions & 19 deletions tests/legacy-cli/e2e/tests/commands/add/add-pwa.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,23 +48,4 @@ export default async function () {

await ng('build');
await expectFileToExist(ngswPath);

// It should correctly generate assetGroups and include at least one URL in each group.
const ngswJson = JSON.parse(await readFile(ngswPath));
// @ts-ignore
const assetGroups: any[] = ngswJson.assetGroups.map(({ name, urls }) => ({
name,
urlCount: urls.length,
}));
const emptyAssetGroups = assetGroups.filter(({ urlCount }) => urlCount === 0);

if (assetGroups.length === 0) {
throw new Error("Expected 'ngsw.json' to contain at least one asset-group.");
}
if (emptyAssetGroups.length > 0) {
throw new Error(
'Expected all asset-groups to contain at least one URL, but the following groups are empty: ' +
emptyAssetGroups.map(({ name }) => name).join(', '),
);
}
}
8 changes: 4 additions & 4 deletions tests/legacy-cli/e2e/tests/commands/config/config-get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,17 @@ export default async function () {
'config',
`projects.test-project.architect.build.options.assets[0]`,
);
if (!stdout2.includes('src/favicon.ico')) {
throw new Error(`Expected "src/favicon.ico", received "${JSON.stringify(stdout)}".`);
if (!stdout2.includes('"input": "src/public"')) {
throw new Error(`Expected "input": "src/public", received "${JSON.stringify(stdout)}".`);
}

const { stdout: stdout3 } = await ng(
'config',
`projects["test-project"].architect.build.options.assets[0]`,
);

if (!stdout3.includes('src/favicon.ico')) {
throw new Error(`Expected "src/favicon.ico", received "${JSON.stringify(stdout)}".`);
if (!stdout3.includes('"input": "src/public"')) {
throw new Error(`Expected "input": "src/public", received "${JSON.stringify(stdout)}".`);
}

// should print all config when no positional args are provided.
Expand Down
12 changes: 6 additions & 6 deletions tests/legacy-cli/e2e/tests/commands/serve/assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ export default async function () {
const outsideDirectoryName = `../outside-${randomUUID()}`;

await updateJsonFile('angular.json', (json) => {
json.projects['test-project'].architect.build.options.assets = [
'src/favicon.ico',
'src/assets',
// Ensure assets located outside the workspace root work with the dev server
{ 'input': outsideDirectoryName, 'glob': '**/*', 'output': './outside' },
];
// Ensure assets located outside the workspace root work with the dev server
json.projects['test-project'].architect.build.options.assets.push({
'input': outsideDirectoryName,
'glob': '**/*',
'output': './outside',
});
});

await mkdir(outsideDirectoryName);
Expand Down

0 comments on commit ab8bac4

Please sign in to comment.