Skip to content

Commit 5263672

Browse files
committed
feat(UST-1087): create package
1 parent ee234d8 commit 5263672

33 files changed

+5435
-123
lines changed

.changeset/brown-clouds-kneel.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@vue-storefront/nuxt": major
3+
---
4+
5+
A new version of @vue-storefront/nuxt dedicated for Vue Storefront 2

.eslintignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
**/node_modules/**/*
22
**/lib/*
3+
**/dist/**/*
34
!**/.vuepress/**/*
45
packages/cache/nuxt/plugin.js
56
packages/nuxt-module/plugins/i18n-cookies.js
67
packages/nuxt-module/plugins/logger.js
78
packages/cli/dist
8-
packages/middleware/__tests__/unit/test-data/*
9+
packages/middleware/__tests__/unit/test-data/*

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ lerna-debug.log*
1515

1616
# Rollup generate output
1717
lib
18+
dist
1819

1920
# Coverage directory used by tools like istanbul
2021
coverage

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"@vue-storefront/jest-config": "^0.0.3",
2424
"@vue-storefront/rollup-config": "^0.0.6",
2525
"cross-env": "^6.0.3",
26+
"cypress": "^13.6.1",
2627
"husky": "^8.0.3",
2728
"jest": "^27.0.6",
2829
"lerna": "^6.6.2",

packages/nuxt/.eslintignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
dist

packages/nuxt/README.md

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# @vue-storefront/sdk-nuxt
2+
3+
## Quick Setup
4+
5+
1. Add `@vue-storefront/sdk-nuxt` dependency to your project
6+
7+
```bash
8+
# Using pnpm
9+
pnpm add -D @vue-storefront/sdk-nuxt
10+
11+
# Using yarn
12+
yarn add --dev @vue-storefront/sdk-nuxt
13+
14+
# Using npm
15+
npm install --save-dev @vue-storefront/sdk-nuxt
16+
```
17+
18+
2. Add `@vue-storefront/sdk-nuxt` to the `modules` section of `nuxt.config.ts`
19+
20+
```js
21+
export default defineNuxtConfig({
22+
modules: ["@vue-storefront/sdk-nuxt"],
23+
});
24+
```
25+
26+
3. Configure the module
27+
28+
There are two ways you can configure the SDK module. The first is by using the `vsfSdk` key in the Nuxt configuration object and providing necessary information such as the Middleware instance address:
29+
30+
```ts
31+
export default defineNuxtConfig({
32+
modules: ["@vue-storefront/sdk-nuxt"],
33+
vsfSdk: {
34+
apiBaseUrl: "localhost:4000",
35+
apiProtocol: "http",
36+
apiSubpath: "",
37+
isMultistoreEnabled: false,
38+
},
39+
});
40+
```
41+
42+
The second is to use Runtime Config. You can set the same set of variables in Runtime Config, providing access to this data throughout the application. You can control the values of these variables through environment variables:
43+
44+
```ts
45+
export default defineNuxtConfig({
46+
modules: ["@vue-storefront/sdk-nuxt"],
47+
runtimeConfig: {
48+
public: {
49+
apiBaseUrl: "localhost:4000",
50+
},
51+
},
52+
});
53+
```
54+
55+
4. Create SDK config file - `sdk.config.ts` in root directory of your project:
56+
57+
The `defineSdkConfig` function is used for this purpose. The parameter for calling this function should be an anonymous function that receives an injected context from the module, containing:
58+
59+
- the `buildModule` function,
60+
- the middleware URL (`middlewareUrl`),
61+
- a function for retrieving the Set-Cookie header with cookie values (`getCookieHeader`).
62+
- a function that compose Middleware URL - in case you want to do it by yourself (`composeMiddlewareUrl`)
63+
- a module config
64+
65+
You should import all other SDK configuration components. See the example below illustrating the SDK configuration with Unified and Contentful modules.
66+
67+
```ts
68+
import {
69+
contentfulModule,
70+
ContentfulModuleType,
71+
} from "@vsf-enterprise/contentful-sdk";
72+
import { unifiedModule } from "@vsf-enterprise/unified-sdk";
73+
import type { UnifiedApiExtension } from "../storefront-middleware/middleware.config";
74+
75+
export default defineSdkConfig(
76+
({ buildModule, middlewareUrl, getCookieHeader }) => ({
77+
unified: buildModule(unifiedModule<UnifiedApiExtension>, {
78+
apiUrl: middlewareUrl + "/commerce",
79+
requestOptions: {
80+
headers: getCookieHeader,
81+
},
82+
}),
83+
contentful: buildModule<ContentfulModuleType>(contentfulModule, {
84+
apiUrl: middlewareUrl + "/cntf",
85+
}),
86+
}),
87+
);
88+
```
89+
90+
That's it! You can now use VueStorefront SDK in your Nuxt app ✨

packages/nuxt/__tests__/app/app.vue

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<template>
2+
<div>
3+
<strong>Success</strong>
4+
<div id="result" class="mt-4">
5+
{{ result }}
6+
</div>
7+
</div>
8+
</template>
9+
10+
<script setup>
11+
const sdk = useSdk();
12+
13+
const result = ref("null");
14+
15+
async function testCall() {
16+
const data = await sdk.example.getSuccess();
17+
result.value = JSON.stringify(data);
18+
}
19+
20+
await testCall();
21+
</script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export default defineNuxtConfig({
2+
modules: ["../../src/module.ts"],
3+
vsfSdk: {
4+
apiBaseUrl: "localhost:4000",
5+
apiProtocol: "http",
6+
apiSubpath: "",
7+
isMultistoreEnabled: false,
8+
},
9+
});
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"private": true,
3+
"name": "@vue-storefront/nuxt-tests-app",
4+
"type": "module",
5+
"scripts": {
6+
"dev": "nuxt dev",
7+
"postinstall": "nuxt prepare"
8+
},
9+
"devDependencies": {
10+
"nuxt": "3.7.4",
11+
"@vue-storefront/sdk": "*"
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// eslint-disable-next-line import/no-relative-packages
2+
import { exampleSdkModule } from "../sdk";
3+
4+
export default defineSdkConfig(({ buildModule, middlewareUrl }) => ({
5+
example: buildModule(exampleSdkModule, {
6+
apiUrl: `${middlewareUrl}/test_integration`,
7+
}),
8+
}));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"extends": "./.nuxt/tsconfig.json"
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
describe("SDK Module Acceptance Criteria", () => {
2+
beforeEach(() => {
3+
cy.visit("http://localhost:3000");
4+
});
5+
6+
it("should have meta tag on ssr rendered page", () => {
7+
cy.get("head meta[name=generator]").should(
8+
"have.attr",
9+
"content",
10+
"vue storefront 2"
11+
);
12+
});
13+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/// <reference types="cypress" />
2+
// ***********************************************
3+
// This example commands.ts shows you how to
4+
// create various custom commands and overwrite
5+
// existing commands.
6+
//
7+
// For more comprehensive examples of custom
8+
// commands please read more here:
9+
// https://on.cypress.io/custom-commands
10+
// ***********************************************
11+
//
12+
//
13+
// -- This is a parent command --
14+
// Cypress.Commands.add('login', (email, password) => { ... })
15+
//
16+
//
17+
// -- This is a child command --
18+
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
19+
//
20+
//
21+
// -- This is a dual command --
22+
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
23+
//
24+
//
25+
// -- This will overwrite an existing command --
26+
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
27+
//
28+
// declare global {
29+
// namespace Cypress {
30+
// interface Chainable {
31+
// login(email: string, password: string): Chainable<void>
32+
// drag(subject: string, options?: Partial<TypeOptions>): Chainable<Element>
33+
// dismiss(subject: string, options?: Partial<TypeOptions>): Chainable<Element>
34+
// visit(originalFn: CommandOriginalFn, url: string, options: Partial<VisitOptions>): Chainable<Element>
35+
// }
36+
// }
37+
// }
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// ***********************************************************
2+
// This example support/e2e.ts is processed and
3+
// loaded automatically before your test files.
4+
//
5+
// This is a great place to put global configuration and
6+
// behavior that modifies Cypress.
7+
//
8+
// You can change the location of this file or turn off
9+
// automatically serving support files with the
10+
// 'supportFile' configuration option.
11+
//
12+
// You can read more here:
13+
// https://on.cypress.io/configuration
14+
// ***********************************************************
15+
16+
// Import commands.js using ES2015 syntax:
17+
import "./commands";
18+
19+
// Alternatively you can use CommonJS syntax:
20+
// require('./commands')
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { MiddlewareConfig } from "@vue-storefront/middleware";
2+
3+
export default {
4+
integrations: {
5+
test_integration: {
6+
location: "../../../middleware/__tests__/integration/bootstrap/server",
7+
configuration: {},
8+
},
9+
},
10+
} satisfies MiddlewareConfig;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"watch": ["**/*"],
3+
"ext": "ts",
4+
"exec": "ts-node-dev src/index.ts"
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"private": true,
3+
"name": "@vue-storefront/nuxt-tests-middleware",
4+
"scripts": {
5+
"dev": "nodemon"
6+
},
7+
"devDependencies": {
8+
"@types/express": "^4.17.13",
9+
"@vue-storefront/middleware": "*",
10+
"express": "^4.18.1",
11+
"nodemon": "^3.0.1",
12+
"ts-node-dev": "^2.0.0"
13+
}
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { createServer } from "@vue-storefront/middleware";
2+
import config from "../middleware.config";
3+
4+
const port = 4000;
5+
6+
(async () => {
7+
const server = await createServer(config);
8+
server.listen(port, "", () => {
9+
console.log(`[test-middleware] API server listening on port ${port}`);
10+
});
11+
})();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"compilerOptions": {},
3+
"include": ["**/*.ts"],
4+
"exclude": ["dist", "lib", "node_modules", ".turbo"]
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import type { Module } from "@vue-storefront/sdk";
2+
3+
interface Options {
4+
apiUrl: string;
5+
}
6+
7+
export function exampleSdkModule({ apiUrl }: Options) {
8+
return {
9+
connector: {
10+
getSuccess() {
11+
return fetch(`${apiUrl}/success`, { method: "POST" }).then((res) =>
12+
res.json()
13+
);
14+
},
15+
},
16+
utils: {},
17+
subscribers: {},
18+
} satisfies Module;
19+
}

packages/nuxt/__tests__/sdk/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from "./exampleSdkModule";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { describe, it, expect } from "vitest";
2+
import { composeMiddlewareUrl } from "../../src/runtime/utils/composeMiddlewareUrl";
3+
4+
describe("SDK utils", () => {
5+
describe("composeMiddlewareUrl", () => {
6+
describe("single store", () => {
7+
it("uses config values", () => {
8+
const composedUrl = composeMiddlewareUrl({
9+
config: {
10+
apiBaseUrl: "localhost:8080",
11+
apiProtocol: "https",
12+
apiSubpath: "/api",
13+
isMultistoreEnabled: false,
14+
},
15+
headers: {},
16+
clientsideUrl: null,
17+
});
18+
expect(composedUrl).toBe("https://localhost:8080/api");
19+
});
20+
});
21+
describe("multistore", () => {
22+
it('uses "host" header ', () => {
23+
const composedUrl = composeMiddlewareUrl({
24+
config: {
25+
apiBaseUrl: "localhost:8080",
26+
apiProtocol: "https",
27+
apiSubpath: "/api",
28+
isMultistoreEnabled: true,
29+
},
30+
headers: {
31+
host: "incoming-url",
32+
},
33+
clientsideUrl: null,
34+
});
35+
expect(composedUrl).toBe("https://incoming-url/api");
36+
});
37+
it("uses original url when request has been proxied", () => {
38+
const composedUrl = composeMiddlewareUrl({
39+
config: {
40+
apiBaseUrl: "localhost:8080",
41+
apiProtocol: "https",
42+
apiSubpath: "/api",
43+
isMultistoreEnabled: true,
44+
},
45+
headers: {
46+
"x-forwarded-host": "original-url",
47+
host: "proxy-url",
48+
},
49+
clientsideUrl: null,
50+
});
51+
expect(composedUrl).toBe("https://original-url/api");
52+
});
53+
it("uses local url when available", () => {
54+
const composedUrl = composeMiddlewareUrl({
55+
config: {
56+
apiBaseUrl: "localhost:8080",
57+
apiProtocol: "https",
58+
apiSubpath: "/api",
59+
isMultistoreEnabled: true,
60+
},
61+
headers: {},
62+
clientsideUrl: "local-url",
63+
});
64+
expect(composedUrl).toBe("https://local-url/api");
65+
});
66+
});
67+
});
68+
});

0 commit comments

Comments
 (0)