Skip to content
This repository has been archived by the owner on Jan 28, 2025. It is now read-only.

Commit

Permalink
feat(lambda-at-edge): detect and copy next-i18next files (#1472)
Browse files Browse the repository at this point in the history
  • Loading branch information
dphang authored Jul 31, 2021
1 parent a5d74d2 commit 3ffc938
Show file tree
Hide file tree
Showing 40 changed files with 252 additions and 0 deletions.
15 changes: 15 additions & 0 deletions packages/libs/lambda-at-edge/src/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { Item } from "klaw";
import { Job } from "@vercel/nft/out/node-file-trace";
import { prepareBuildManifests } from "@sls-next/core";
import { NextConfig } from "@sls-next/core/dist/build";
import { NextI18nextIntegration } from "./build/third-party/next-i18next";

export const DEFAULT_LAMBDA_CODE_DIR = "default-lambda";
export const API_LAMBDA_CODE_DIR = "api-lambda";
Expand Down Expand Up @@ -331,6 +332,9 @@ class Builder {
this.processAndCopyRoutesManifest(
join(this.dotNextDir, "routes-manifest.json"),
join(this.outputDir, DEFAULT_LAMBDA_CODE_DIR, "routes-manifest.json")
),
this.runThirdPartyIntegrations(
join(this.outputDir, DEFAULT_LAMBDA_CODE_DIR)
)
]);
}
Expand Down Expand Up @@ -785,6 +789,17 @@ class Builder {
// Copy static assets to .serverless_nextjs directory
await this.buildStaticAssets(defaultBuildManifest, routesManifest);
}

/**
* Run additional integrations for third-party libraries such as next-i18next.
* These are usually needed to add additional files into the lambda, etc.
* @param outputLambdaDir
*/
async runThirdPartyIntegrations(outputLambdaDir: string): Promise<void> {
await Promise.all([
new NextI18nextIntegration(this.nextConfigDir, outputLambdaDir).execute()
]);
}
}

export default Builder;
3 changes: 3 additions & 0 deletions packages/libs/lambda-at-edge/src/build/third-party/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## Third party integrations

This will support common third-party integrations such as next-i18next. The integrations here are meant to auto-detect what your project is using and copy the appropriate files to the build artifacts.
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { join } from "path";
import fse from "fs-extra";

export abstract class ThirdPartyIntegrationBase {
nextConfigDir: string;
outputLambdaDir: string;

constructor(nextConfigDir: string, outputLambdaDir: string) {
this.nextConfigDir = nextConfigDir;
this.outputLambdaDir = outputLambdaDir;
}

abstract execute(): void;

async isPackagePresent(name: string): Promise<boolean> {
const packageJsonPath = join(this.nextConfigDir, "package.json");

if (await fse.pathExists(packageJsonPath)) {
const packageJson = await fse.readJSON(packageJsonPath);
return !!packageJson.dependencies[name];
}

return false;
}
}
32 changes: 32 additions & 0 deletions packages/libs/lambda-at-edge/src/build/third-party/next-i18next.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import fse from "fs-extra";
import { join } from "path";
import { ThirdPartyIntegrationBase } from "./integration-base";

export class NextI18nextIntegration extends ThirdPartyIntegrationBase {
/**
* This will copy all next-i18next files as needed to a lambda directory.
*/
async execute(): Promise<void> {
if (await this.isPackagePresent("next-i18next")) {
const localeSrc = join(this.nextConfigDir, "public", "locales");
const localeDest = join(this.outputLambdaDir, "public", "locales");

if (await fse.pathExists(localeSrc)) {
await fse.copy(localeSrc, localeDest, { recursive: true });
}

const nextI18nextConfigSrc = join(
this.nextConfigDir,
"next-i18next.config.js"
);
const nextI18nextConfigDest = join(
this.outputLambdaDir,
"next-i18next.config.js"
);

if (await fse.pathExists(nextI18nextConfigSrc)) {
await fse.copy(nextI18nextConfigSrc, nextI18nextConfigDest);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { join } from "path";
import fse from "fs-extra";
import execa from "execa";
import Builder, {
DEFAULT_LAMBDA_CODE_DIR,
API_LAMBDA_CODE_DIR,
IMAGE_LAMBDA_CODE_DIR
} from "../../src/build";
import { cleanupDir } from "../test-utils";
import {
OriginRequestDefaultHandlerManifest,
OriginRequestApiHandlerManifest,
OriginRequestImageHandlerManifest
} from "../../src/types";

jest.mock("execa");

describe("Builder Tests (with third party integrations)", () => {
let fseRemoveSpy: jest.SpyInstance;
let fseEmptyDirSpy: jest.SpyInstance;
let defaultBuildManifest: OriginRequestDefaultHandlerManifest;
let apiBuildManifest: OriginRequestApiHandlerManifest;
let imageBuildManifest: OriginRequestImageHandlerManifest;

const fixturePath = join(
__dirname,
"./simple-app-fixture-third-party-integrations"
);
const outputDir = join(fixturePath, ".test_sls_next_output");

describe("Regular build", () => {
beforeEach(async () => {
const mockExeca = execa as jest.Mock;
mockExeca.mockResolvedValueOnce();

fseRemoveSpy = jest.spyOn(fse, "remove").mockImplementation(() => {
return;
});
fseEmptyDirSpy = jest.spyOn(fse, "emptyDir");

const builder = new Builder(fixturePath, outputDir, {});
await builder.build();

defaultBuildManifest = await fse.readJSON(
join(outputDir, `${DEFAULT_LAMBDA_CODE_DIR}/manifest.json`)
);

apiBuildManifest = await fse.readJSON(
join(outputDir, `${API_LAMBDA_CODE_DIR}/manifest.json`)
);

imageBuildManifest = await fse.readJSON(
join(outputDir, `${IMAGE_LAMBDA_CODE_DIR}/manifest.json`)
);
});

afterEach(() => {
fseEmptyDirSpy.mockRestore();
fseRemoveSpy.mockRestore();
//return cleanupDir(outputDir);
});

describe("Default Handler Third Party Files", () => {
it("next-i18next files are copied", async () => {
expect(
await fse.pathExists(
join(
outputDir,
`${DEFAULT_LAMBDA_CODE_DIR}`,
"next-i18next.config.js"
)
)
).toBe(true);

const localesFiles = await fse.readdir(
join(outputDir, `${DEFAULT_LAMBDA_CODE_DIR}`, "public", "locales")
);

expect(localesFiles).toEqual(expect.arrayContaining(["de", "en"]));
});
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test-build-id
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Having a cache/ folder allows to test the cleanup of .next/ except for cache/ for faster builds
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"version": 1,
"images": {
"deviceSizes": [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
"imageSizes": [16, 32, 48, 64, 96, 128, 256, 384],
"domains": [],
"path": "/_next/image",
"loader": "default",
"sizes": [
640,
750,
828,
1080,
1200,
1920,
2048,
3840,
16,
32,
48,
64,
96,
128,
256,
384
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"version": 2,
"routes": {
"/": {
"initialRevalidateSeconds": false,
"srcRoute": null,
"dataRoute": "/_next/data/zsWqBqLjpgRmswfQomanp/index.json"
},
"/contact": {
"initialRevalidateSeconds": false,
"srcRoute": null,
"dataRoute": "/_next/data/zsWqBqLjpgRmswfQomanp/contact.json"
}
},
"dynamicRoutes": {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"version":1,"pages404":true,"basePath":"","redirects":[],"rewrites":[],"headers":[],"dynamicRoutes":[]}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"/[root]": "pages/[root].js",
"/customers/[customer]": "pages/customers/[customer].js",
"/customers/[customer]/[post]": "pages/customers/[customer]/[post].js",
"/customers/new": "pages/customers/new.js",
"/customers/[customer]/profile": "pages/customers/[customer]/profile.js",
"/customers/[...catchAll]": "pages/customers/[...catchAll].js",
"/products/[[...optionalCatchAll]]": "pages/products/[[...optionalCatchAll]].js",
"/api/customers": "pages/api/customers.js",
"/api/customers/[id]": "pages/api/customers/[id].js",
"/api/customers/new": "pages/api/customers/new.js",
"/terms": "pages/terms.html",
"/about": "pages/about.html",
"/blog/[post]": "pages/blog/[post].html",
"/": "pages/index.js",
"/_app": "pages/_app.js",
"/_document": "pages/_document.js",
"/404": "pages/404.html"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const path = require("path");

module.exports = {
i18n: {
defaultLocale: "en",
locales: ["en", "de"],
localePath: path.resolve("./public/locales")
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = { target: "serverless" };
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"dependencies": {
"next-i18next": "^8.5.5"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"button": {
"login": "Login",
"register": "Register"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"button": {
"login": "Login",
"register": "Register"
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// Test handler file to check if copied

0 comments on commit 3ffc938

Please sign in to comment.