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

Make Site.copy support multiple destinations #429

Open
wants to merge 4 commits into
base: v1
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ Any BREAKING CHANGE between minor versions will be documented here in upper case

### Fixed

- `Site.copy` now works as expected when given a path with a trailing slash. [#426]
- `Site.copy` now supports trailing slashes in the source path. [#426]
- Multiple `Site.copy` calls can now be used to copy one file to multiple destinations. [#429]

## [1.17.4] - 2023-05-25
### Added
Expand Down Expand Up @@ -2261,6 +2262,7 @@ The first version.
[#418]: https://github.com/lumeland/lume/issues/418
[#419]: https://github.com/lumeland/lume/issues/419
[#426]: https://github.com/lumeland/lume/pull/426
[#429]: https://github.com/lumeland/lume/pull/429

[1.17.4]: https://github.com/lumeland/lume/compare/v1.17.3...v1.17.4
[1.17.3]: https://github.com/lumeland/lume/compare/v1.17.2...v1.17.3
Expand Down
6 changes: 6 additions & 0 deletions core/fs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,17 @@ export interface Options {
export type Loader = (path: string) => Promise<Data>;

export class Entry {
/** The name of the file/dir. */
name: string;
/** The normalized path of the file/dir. */
path: string;
/** The type of the entry. */
type: EntryType;
/** The absolute file path. */
src: string;
/** The children of the entry. */
children = new Map<string, Entry>();
/** Temporary flags that are cleared when a file is modified. */
flags = new Set<string>();
#content = new Map<Loader, Promise<Data> | Data>();
#info?: Deno.FileInfo;
Expand Down
84 changes: 52 additions & 32 deletions core/source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,11 @@ export default class Source {
prettyUrls: boolean;

/** List of static files and folders to copy */
staticPaths = new Map<
string,
{ dest: string | ((path: string) => string) | undefined; dirOnly: boolean }
>();
staticPaths: {
from: string;
to: string | ((path: string) => string) | undefined;
dirOnly: boolean;
}[] = [];

/** List of static files and folders to copy */
copyRemainingFiles?: (path: string) => string | boolean;
Expand Down Expand Up @@ -102,10 +103,10 @@ export default class Source {
}

addStaticPath(from: string, to?: string | ((path: string) => string)) {
this.staticPaths.set(
normalizePath(from.replace(/\/$/, "")),
this.staticPaths.push(
{
dest: typeof to === "string" ? normalizePath(to) : to,
from: normalizePath(from.replace(/\/$/, "")),
to: typeof to === "string" ? normalizePath(to) : to,
dirOnly: from.endsWith("/"),
},
);
Expand All @@ -118,12 +119,42 @@ export default class Source {
const pages: Page[] = [];
const staticFiles: StaticFile[] = [];

// Resolve staticPaths to staticFiles
for (const instruction of this.staticPaths) {
const entry = this.fs.entries.get(instruction.from);
if (entry) {
const path = posix.dirname(entry.path);

if (entry.type == "file") {
if (instruction.dirOnly) {
continue;
}
staticFiles.push({
entry,
outputPath: getOutputPath(
entry,
path,
instruction.to,
),
});
} else {
const dest = instruction.to;
staticFiles.push(...this.#getStaticFiles(
entry,
typeof dest === "string" ? dest : posix.join(path, entry.name),
typeof dest === "function" ? dest : undefined,
));
}
}
}

await this.#build(
buildFilters,
this.fs.entries.get("/")!,
"/",
globalComponents,
{},
new Set(this.staticPaths.map((p) => p.from)),
pages,
staticFiles,
);
Expand All @@ -140,6 +171,7 @@ export default class Source {
path: string,
parentComponents: Components,
parentData: Data,
staticPaths: Set<string>,
pages: Page[],
staticFiles: StaticFile[],
) {
Expand Down Expand Up @@ -191,26 +223,8 @@ export default class Source {
continue;
}

// Static files
if (this.staticPaths.has(entry.path)) {
const { dest, dirOnly } = this.staticPaths.get(entry.path)!;

if (entry.type === "file") {
if (dirOnly) {
continue;
}
staticFiles.push({
entry,
outputPath: getOutputPath(entry, path, dest),
});
continue;
}

staticFiles.push(...this.#getStaticFiles(
entry,
typeof dest === "string" ? dest : posix.join(path, entry.name),
typeof dest === "function" ? dest : undefined,
));
if (staticPaths.has(entry.path)) {
// The static file has already been resolved.
continue;
}

Expand Down Expand Up @@ -311,6 +325,7 @@ export default class Source {
path,
parentComponents,
dirData,
staticPaths,
pages,
staticFiles,
);
Expand Down Expand Up @@ -691,13 +706,18 @@ export function getOutputPath(
path: string,
dest?: string | ((path: string) => string),
): string {
if (typeof dest === "function") {
return dest(posix.join(path, entry.name));
}

if (typeof dest === "string") {
return dest;
}

return posix.join(path, entry.name);
const outputPath = posix.join(
path.split("/").map((comp) => parseDate(comp)[0]).join("/"),
entry.name,
);

if (typeof dest === "function") {
return dest(outputPath);
}

return outputPath;
}
5 changes: 3 additions & 2 deletions core/writer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ export default class Writer {

/**
* Copy the static files in the dest folder
* Returns the static files that have been successfully copied
*/
async copyFiles(files: StaticFile[]): Promise<StaticFile[]> {
const copyFiles: StaticFile[] = [];
Expand All @@ -135,11 +136,11 @@ export default class Writer {
async copyFile(file: StaticFile): Promise<boolean> {
const { entry } = file;

if (entry.flags.has("saved")) {
if (entry.flags.has("saved-" + file.outputPath)) {
return false;
}

entry.flags.add("saved");
entry.flags.add("saved-" + file.outputPath);

const pathTo = posix.join(this.dest, file.outputPath);

Expand Down
15 changes: 15 additions & 0 deletions tests/__snapshots__/static_files.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,16 @@ snapshot[`Copy static files 2`] = `
flags: [],
outputPath: "/_headers",
},
{
entry: "/one.yes",
flags: [],
outputPath: "/one.yes",
},
{
entry: "/one.yes",
flags: [],
outputPath: "/one-again.yes",
},
{
entry: "/other/one",
flags: [],
Expand Down Expand Up @@ -166,6 +176,11 @@ snapshot[`Copy static files 2`] = `
flags: [],
outputPath: "/script/app/main.js",
},
{
entry: "/static/_redirects",
flags: [],
outputPath: "/_redirects",
},
{
entry: "/static/one.yes",
flags: [],
Expand Down
19 changes: 9 additions & 10 deletions tests/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,19 @@ Deno.test("static files configuration", () => {
const { staticPaths } = site.source;

site.copy("img");
equals(staticPaths.size, 1);
equals(staticPaths.has("/img"), true);
equals(staticPaths.get("/img")!.dest, undefined);
equals(staticPaths.length, 1);
equals(staticPaths[0].from, "/img");
equals(staticPaths[0].to, undefined);

site.copy("statics/favicon.ico", "favicon.ico");
equals(staticPaths.size, 2);
equals(
staticPaths.get("/statics/favicon.ico")!.dest,
"/favicon.ico",
);
equals(staticPaths.length, 2);
equals(staticPaths[1].from, "/statics/favicon.ico");
equals(staticPaths[1].to, "/favicon.ico");

site.copy("css", ".");
equals(staticPaths.size, 3);
equals(staticPaths.get("/css")!.dest, "/");
equals(staticPaths.length, 3);
equals(staticPaths[2].from, "/css");
equals(staticPaths[2].to, "/");
});

Deno.test("ignored files configuration", () => {
Expand Down
4 changes: 4 additions & 0 deletions tests/static_files.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ Deno.test("Copy static files", async (t) => {
(file) => "/subdir" + file.replace(/\.copy2/, ".copy3"),
);

// a single file can be copied multiple times
site.copy("one.yes");
site.copy("one.yes", "one-again.yes");

// copied with the trailing slash
site.copy("other2/");

Expand Down