├── .gitattributes ├── .npmrc ├── templates └── ts-tailwind │ ├── instant.config.json │ ├── src │ ├── vite-env.d.ts │ └── blocks │ │ └── HeroBlock │ │ ├── styles.css │ │ └── index.tsx │ ├── postcss.config.cjs │ ├── vite.config.ts │ ├── tailwind.config.cjs │ ├── tsconfig.node.json │ ├── _gitignore │ ├── package.json │ └── tsconfig.json ├── packages ├── cli │ ├── src │ │ ├── templates │ │ │ ├── index.ts │ │ │ └── block.ts │ │ ├── components │ │ │ ├── index.ts │ │ │ └── SelectInput │ │ │ │ ├── @types │ │ │ │ └── arr-rotate.d.ts │ │ │ │ ├── Item.tsx │ │ │ │ ├── Indicator.tsx │ │ │ │ └── index.tsx │ │ ├── lib │ │ │ ├── api │ │ │ │ ├── index.ts │ │ │ │ ├── api.constants.ts │ │ │ │ ├── fragments │ │ │ │ │ └── block.graphql │ │ │ │ ├── queries │ │ │ │ │ ├── userWithOrgs.graphql │ │ │ │ │ ├── me.graphql │ │ │ │ │ └── stores.graphql │ │ │ │ ├── mutations │ │ │ │ │ ├── createOneBlock.graphql │ │ │ │ │ └── publishBlockVersion.graphql │ │ │ │ ├── extractApiError.ts │ │ │ │ └── useApiSdk.ts │ │ │ ├── auth0 │ │ │ │ ├── abstractions │ │ │ │ │ ├── Auth0ErrorResponse.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── Auth0AccessTokenResponse.ts │ │ │ │ │ └── Auth0DeviceCodeResponse.ts │ │ │ │ ├── index.ts │ │ │ │ ├── auth0.constants.ts │ │ │ │ ├── getDeviceCode.ts │ │ │ │ ├── refreshAccessToken.ts │ │ │ │ └── checkDeviceCode.ts │ │ │ ├── getBlockNameFromPath.ts │ │ │ ├── getBlockFiles.ts │ │ │ ├── getProjectConfig.ts │ │ │ ├── schemaFieldTypeMapping.ts │ │ │ ├── getViteConfig.ts │ │ │ ├── parseCustomizerSchema.ts │ │ │ └── parseContentSchema.ts │ │ ├── env.ts │ │ ├── commands │ │ │ ├── index.ts │ │ │ ├── logout.tsx │ │ │ ├── whoami.tsx │ │ │ ├── refresh.tsx │ │ │ ├── generate.tsx │ │ │ ├── login.tsx │ │ │ ├── select.tsx │ │ │ ├── init.tsx │ │ │ └── add.tsx │ │ ├── test.tsx │ │ ├── cli.ts │ │ └── config.ts │ ├── .env.example │ ├── tsup.config.ts │ ├── tsconfig.json │ ├── README.md │ └── package.json ├── preview │ ├── src │ │ ├── containers │ │ │ ├── index.ts │ │ │ ├── App.tsx │ │ │ └── Preview │ │ │ │ ├── components.tsx │ │ │ │ ├── storeFallback.ts │ │ │ │ ├── index.tsx │ │ │ │ ├── previewSchema.ts │ │ │ │ ├── getStore.ts │ │ │ │ ├── sandbox.ts │ │ │ │ └── Head.tsx │ │ ├── vite-env.d.ts │ │ ├── index.css │ │ ├── components │ │ │ ├── BlocksProvider │ │ │ │ ├── useBlocks.ts │ │ │ │ └── context.ts │ │ │ ├── ConfigProvider │ │ │ │ ├── useConfig.tsx │ │ │ │ └── context.tsx │ │ │ ├── Input │ │ │ │ ├── Label.tsx │ │ │ │ ├── index.tsx │ │ │ │ └── InputWrapper.tsx │ │ │ ├── index.ts │ │ │ ├── InputGroup │ │ │ │ └── index.tsx │ │ │ ├── Resizable │ │ │ │ ├── Handle.tsx │ │ │ │ └── index.tsx │ │ │ ├── ImageInput │ │ │ │ └── index.tsx │ │ │ ├── PreviewWrapper.tsx │ │ │ ├── ToggleGroup │ │ │ │ └── index.tsx │ │ │ ├── Toggle │ │ │ │ └── index.tsx │ │ │ ├── Layout │ │ │ │ ├── RightPanel │ │ │ │ │ └── Breadcrumbs.tsx │ │ │ │ ├── SideBar.tsx │ │ │ │ ├── TopBar.tsx │ │ │ │ └── Logo.tsx │ │ │ ├── RichText │ │ │ │ └── index.tsx │ │ │ ├── Tooltip │ │ │ │ └── index.tsx │ │ │ ├── Modal │ │ │ │ └── index.tsx │ │ │ ├── PreviewFrame.tsx │ │ │ ├── Slider │ │ │ │ └── index.tsx │ │ │ ├── StatusMessage.tsx │ │ │ ├── Tabs │ │ │ │ └── index.tsx │ │ │ ├── Button │ │ │ │ └── index.tsx │ │ │ ├── ColorInput │ │ │ │ └── index.tsx │ │ │ ├── Toast.tsx │ │ │ └── Select │ │ │ │ └── index.tsx │ │ ├── main.tsx │ │ └── shims.d.ts │ ├── public │ │ └── favicon.ico │ ├── postcss.config.cjs │ ├── tsconfig.node.json │ ├── preview.html │ ├── tsconfig.json │ ├── index.html │ ├── vite.config.ts │ ├── package.json │ └── tailwind.config.cjs ├── client │ ├── src │ │ ├── components │ │ │ ├── index.ts │ │ │ ├── Link.tsx │ │ │ └── RichText.tsx │ │ ├── utils.ts │ │ ├── index.ts │ │ ├── hooks │ │ │ ├── useCart.ts │ │ │ ├── useToast.ts │ │ │ ├── useCustomer.ts │ │ │ ├── useRequestData.ts │ │ │ ├── useBlockState.ts │ │ │ ├── index.ts │ │ │ ├── useTheme.ts │ │ │ ├── useShopifyClient.ts │ │ │ └── useEventListener.ts │ │ ├── BlockProvider │ │ │ ├── useBlockContext.ts │ │ │ ├── index.tsx │ │ │ └── context.ts │ │ ├── gql.ts │ │ └── defineBlock.ts │ ├── tsconfig.json │ ├── tsup.config.ts │ └── package.json ├── types │ ├── package.json │ └── schemas.ts ├── tsconfig │ ├── README.md │ ├── package.json │ ├── react-library.json │ └── base.json ├── vite-plugin │ ├── tsconfig.json │ ├── tsup.config.ts │ ├── package.json │ └── src │ │ └── fast-refresh.ts └── postcss-plugin │ ├── tsup.config.ts │ ├── tsconfig.json │ ├── package.json │ └── src │ └── index.ts ├── .prettierignore ├── examples └── block-extension │ ├── src │ ├── vite-env.d.ts │ └── blocks │ │ ├── test.css │ │ ├── HeroBlock │ │ ├── index.css │ │ └── index.tsx │ │ └── CustomBlock │ │ ├── styles.css │ │ ├── index.tsx │ │ └── block.tsx │ ├── postcss.config.cjs │ ├── vite.config.ts │ ├── tailwind.config.cjs │ ├── tsconfig.node.json │ ├── instant.config.json │ ├── .gitignore │ ├── tsconfig.json │ └── package.json ├── .eslintignore ├── .gitignore ├── commitlint.config.js ├── turbo.json ├── .graphqlrc.yml ├── .github └── workflows │ └── release.yml ├── .vscode └── settings.json ├── README.md ├── .lintstagedrc.js ├── .releaserc.json └── package.json /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | legacy-peer-deps = false 2 | -------------------------------------------------------------------------------- /templates/ts-tailwind/instant.config.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /packages/cli/src/templates/index.ts: -------------------------------------------------------------------------------- 1 | export * from './block'; 2 | -------------------------------------------------------------------------------- /packages/preview/src/containers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './App'; 2 | -------------------------------------------------------------------------------- /packages/cli/src/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./SelectInput"; 2 | -------------------------------------------------------------------------------- /packages/preview/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /templates/ts-tailwind/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | public/* 3 | dist 4 | coverage 5 | types/api.ts 6 | -------------------------------------------------------------------------------- /examples/block-extension/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /examples/block-extension/src/blocks/test.css: -------------------------------------------------------------------------------- 1 | div a { 2 | text-decoration: overline; 3 | } 4 | -------------------------------------------------------------------------------- /packages/client/src/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Link'; 2 | export * from './RichText'; 3 | -------------------------------------------------------------------------------- /packages/client/src/utils.ts: -------------------------------------------------------------------------------- 1 | export const reload = () => { 2 | (self as any).reload(); 3 | }; 4 | -------------------------------------------------------------------------------- /packages/cli/src/lib/api/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./useApiSdk"; 2 | export * from "./extractApiError"; 3 | -------------------------------------------------------------------------------- /packages/cli/.env.example: -------------------------------------------------------------------------------- 1 | AUTH0_DOMAIN=xxx.xx.auth0.com 2 | AUTH0_CLIENT_ID=xxx 3 | API_URL=http://localhost:4000 -------------------------------------------------------------------------------- /packages/types/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "types", 3 | "version": "0.0.0", 4 | "private": true 5 | } 6 | -------------------------------------------------------------------------------- /examples/block-extension/src/blocks/HeroBlock/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /templates/ts-tailwind/src/blocks/HeroBlock/styles.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /packages/preview/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/instantcommerce/instant-sdk/HEAD/packages/preview/public/favicon.ico -------------------------------------------------------------------------------- /packages/cli/src/lib/api/api.constants.ts: -------------------------------------------------------------------------------- 1 | export const API_URL = 2 | process.env['API_URL'] ?? 'https://api.instantcommerce.app'; 3 | -------------------------------------------------------------------------------- /packages/preview/postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /packages/cli/src/lib/api/fragments/block.graphql: -------------------------------------------------------------------------------- 1 | fragment BlockFragment on Block { 2 | id 3 | name 4 | version { 5 | tag 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tsconfig/react-library.json", 3 | "compilerOptions": { 4 | "baseUrl": "./" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/tsconfig/README.md: -------------------------------------------------------------------------------- 1 | # `tsconfig` 2 | 3 | These are base shared `tsconfig.json`s from which all other `tsconfig.json`'s inherit from. 4 | -------------------------------------------------------------------------------- /templates/ts-tailwind/postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /examples/block-extension/postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | public/* 3 | dist 4 | 5 | packages/types/api.ts 6 | 7 | **/*/hooks.ts 8 | **/*/sdk.ts 9 | **/*/schema.json 10 | -------------------------------------------------------------------------------- /packages/cli/src/lib/auth0/abstractions/Auth0ErrorResponse.ts: -------------------------------------------------------------------------------- 1 | export interface Auth0ErrorResponse { 2 | error: string; 3 | error_description: string; 4 | } 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | **/dist 4 | *.local 5 | .env* 6 | yarn-error.log 7 | _* 8 | !_gitignore 9 | .turbo 10 | 11 | */coverage 12 | */stats.html 13 | -------------------------------------------------------------------------------- /templates/ts-tailwind/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | 3 | // https://vitejs.dev/config/ 4 | export default defineConfig({ 5 | plugins: [], 6 | }); 7 | -------------------------------------------------------------------------------- /packages/client/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './components'; 2 | export * from './defineBlock'; 3 | export * from './gql'; 4 | export * from './hooks'; 5 | export * from './utils'; 6 | -------------------------------------------------------------------------------- /packages/cli/src/lib/auth0/abstractions/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Auth0AccessTokenResponse"; 2 | export * from "./Auth0DeviceCodeResponse"; 3 | export * from "./Auth0ErrorResponse"; 4 | -------------------------------------------------------------------------------- /packages/cli/src/lib/api/queries/userWithOrgs.graphql: -------------------------------------------------------------------------------- 1 | query userWithOrgs { 2 | me { 3 | organizations { 4 | id 5 | name 6 | slug 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/cli/src/lib/auth0/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./getDeviceCode"; 2 | export * from "./checkDeviceCode"; 3 | export * from "./refreshAccessToken"; 4 | export * from "./abstractions"; 5 | -------------------------------------------------------------------------------- /packages/cli/src/lib/getBlockNameFromPath.ts: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | 3 | export const getBlockNameFromPath = (filePath: string) => 4 | path.dirname(filePath).split(path.sep).pop()!; 5 | -------------------------------------------------------------------------------- /examples/block-extension/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | 3 | // https://vitejs.dev/config/ 4 | export default defineConfig(({ mode }) => ({ 5 | plugins: [], 6 | })); 7 | -------------------------------------------------------------------------------- /packages/cli/src/components/SelectInput/@types/arr-rotate.d.ts: -------------------------------------------------------------------------------- 1 | declare module "arr-rotate" { 2 | function arrayRotate(input: T[], number: number): T[]; 3 | export = arrayRotate; 4 | } 5 | -------------------------------------------------------------------------------- /packages/cli/src/lib/api/mutations/createOneBlock.graphql: -------------------------------------------------------------------------------- 1 | mutation createOneBlock($input: CreateOneBlockInput!) { 2 | createOneBlock(input: $input) { 3 | ...BlockFragment 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /packages/cli/src/lib/api/queries/me.graphql: -------------------------------------------------------------------------------- 1 | query me { 2 | me { 3 | firstName 4 | lastName 5 | email 6 | organizations { 7 | slug 8 | name 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/vite-plugin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tsconfig/react-library.json", 3 | "compilerOptions": { 4 | "target": "ESNext", 5 | "baseUrl": "./" 6 | }, 7 | "include": ["./src"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/cli/src/lib/api/mutations/publishBlockVersion.graphql: -------------------------------------------------------------------------------- 1 | mutation publishBlockVersion($input: PublishBlockVersionInput!) { 2 | publishBlockVersion(input: $input) { 3 | ...BlockFragment 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /packages/tsconfig/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tsconfig", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "index.js", 6 | "files": [ 7 | "base.json", 8 | "react.json" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@commitlint/config-conventional'], 3 | rules: { 4 | 'header-max-length': [1, 'always', 72], 5 | 'body-max-line-length': [1, 'always', 80], 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /packages/client/src/hooks/useCart.ts: -------------------------------------------------------------------------------- 1 | import { useBlockContext } from '../BlockProvider'; 2 | 3 | export function useCart() { 4 | const { instantObject } = useBlockContext(); 5 | 6 | return instantObject.cart; 7 | } 8 | -------------------------------------------------------------------------------- /packages/client/src/hooks/useToast.ts: -------------------------------------------------------------------------------- 1 | import { useBlockContext } from '../BlockProvider'; 2 | 3 | export function useToast() { 4 | const { instantObject } = useBlockContext(); 5 | 6 | return instantObject.Toast; 7 | } 8 | -------------------------------------------------------------------------------- /examples/block-extension/tailwind.config.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ['./src/**/*.{js,ts,jsx,tsx}'], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | }; 9 | -------------------------------------------------------------------------------- /templates/ts-tailwind/tailwind.config.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ['./src/**/*.{js,ts,jsx,tsx}'], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | }; 9 | -------------------------------------------------------------------------------- /packages/client/src/hooks/useCustomer.ts: -------------------------------------------------------------------------------- 1 | import { useBlockContext } from '../BlockProvider'; 2 | 3 | export function useCustomer() { 4 | const { instantObject } = useBlockContext(); 5 | 6 | return instantObject.customer; 7 | } 8 | -------------------------------------------------------------------------------- /examples/block-extension/src/blocks/CustomBlock/styles.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | div { 6 | background-color: yellow; 7 | } 8 | 9 | div a { 10 | font-weight: bold; 11 | } 12 | -------------------------------------------------------------------------------- /packages/client/src/hooks/useRequestData.ts: -------------------------------------------------------------------------------- 1 | import { useBlockContext } from '../BlockProvider'; 2 | 3 | export function useRequestData() { 4 | const { instantObject } = useBlockContext(); 5 | 6 | return instantObject.request; 7 | } 8 | -------------------------------------------------------------------------------- /packages/client/src/hooks/useBlockState.ts: -------------------------------------------------------------------------------- 1 | import { useBlockContext } from '../BlockProvider'; 2 | 3 | export function useBlockState() { 4 | const { content, customizer } = useBlockContext(); 5 | 6 | return { content, customizer }; 7 | } 8 | -------------------------------------------------------------------------------- /packages/postcss-plugin/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tsup'; 2 | 3 | export default defineConfig({ 4 | entry: ['src/index.ts'], 5 | splitting: false, 6 | clean: true, 7 | dts: false, 8 | format: ['cjs'], 9 | }); 10 | -------------------------------------------------------------------------------- /packages/cli/src/lib/auth0/abstractions/Auth0AccessTokenResponse.ts: -------------------------------------------------------------------------------- 1 | export interface Auth0AccessTokenResponse { 2 | access_token: string; 3 | refresh_token?: string; 4 | id_token?: string; 5 | token_type: string; 6 | expires_in: number; 7 | } 8 | -------------------------------------------------------------------------------- /packages/cli/src/lib/auth0/auth0.constants.ts: -------------------------------------------------------------------------------- 1 | export const AUTH0_DOMAIN = 2 | process.env['AUTH0_DOMAIN'] ?? 'instantcommerce.eu.auth0.com'; 3 | export const AUTH0_CLIENT_ID = 4 | process.env['AUTH0_CLIENT_ID'] ?? 'iw82AZuS69fHuh1piCd3Q7ZSML3q1j1l'; 5 | -------------------------------------------------------------------------------- /packages/preview/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /examples/block-extension/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /templates/ts-tailwind/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/postcss-plugin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tsconfig/react-library.json", 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "module": "CommonJS", 6 | "outDir": "./dist", 7 | "noEmit": false 8 | }, 9 | "include": ["./src"] 10 | } 11 | -------------------------------------------------------------------------------- /packages/tsconfig/react-library.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "React lib", 4 | "extends": "./base.json", 5 | "compilerOptions": { 6 | "jsx": "react-jsx" 7 | }, 8 | "exclude": ["node_modules"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/cli/src/env.ts: -------------------------------------------------------------------------------- 1 | import { dirname } from 'path'; 2 | import { fileURLToPath } from 'url'; 3 | import * as dotenv from 'dotenv'; 4 | 5 | const __filename = fileURLToPath(import.meta.url); 6 | const __dirname = dirname(__filename); 7 | dotenv.config({ path: `${__dirname}/.env` }); 8 | -------------------------------------------------------------------------------- /packages/cli/src/lib/api/queries/stores.graphql: -------------------------------------------------------------------------------- 1 | query stores { 2 | stores { 3 | edges { 4 | node { 5 | id 6 | name 7 | slug 8 | domains { 9 | isPrimary 10 | hostname 11 | } 12 | } 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/cli/src/lib/auth0/abstractions/Auth0DeviceCodeResponse.ts: -------------------------------------------------------------------------------- 1 | export interface Auth0DeviceCodeResponse { 2 | device_code: string; 3 | user_code: string; 4 | verification_uri: string; 5 | expires_in: number; 6 | interval: number; 7 | verification_uri_complete: string; 8 | } 9 | -------------------------------------------------------------------------------- /packages/preview/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | input[type="number"]::-webkit-inner-spin-button, 7 | input[type="number"]::-webkit-outer-spin-button { 8 | -webkit-appearance: none; 9 | margin: 0; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/client/src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export * from './useBlockState'; 2 | export * from './useCart'; 3 | export * from './useCustomer'; 4 | export * from './useEventListener'; 5 | export * from './useRequestData'; 6 | export * from './useShopifyClient'; 7 | export * from './useTheme'; 8 | export * from './useToast'; 9 | -------------------------------------------------------------------------------- /packages/cli/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tsup'; 2 | 3 | export default defineConfig({ 4 | entry: ['src/cli.ts'], 5 | splitting: false, 6 | clean: true, 7 | dts: false, 8 | format: ['esm'], 9 | env: { 10 | NODE_ENV: 'production', 11 | }, 12 | noExternal: ['types'], 13 | }); 14 | -------------------------------------------------------------------------------- /packages/cli/src/commands/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./dev"; 2 | export * from "./generate"; 3 | export * from "./init"; 4 | export * from "./login"; 5 | export * from "./whoami"; 6 | export * from "./logout"; 7 | export * from "./publish"; 8 | export * from "./refresh"; 9 | export * from "./select"; 10 | export * from "./add"; 11 | -------------------------------------------------------------------------------- /packages/client/src/hooks/useTheme.ts: -------------------------------------------------------------------------------- 1 | import { useBlockContext } from '../BlockProvider'; 2 | 3 | /** 4 | * Get the current storefront's theme, consisting of colors and typography. 5 | */ 6 | export function useTheme() { 7 | const { store } = useBlockContext(); 8 | 9 | return store?.storefront?.config?.theme; 10 | } 11 | -------------------------------------------------------------------------------- /packages/client/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tsup'; 2 | 3 | export default defineConfig({ 4 | entry: ['src/index.ts'], 5 | splitting: false, 6 | clean: true, 7 | dts: { 8 | entry: 'src/index.ts', 9 | }, 10 | format: ['esm', 'cjs'], 11 | env: { 12 | NODE_ENV: 'production', 13 | }, 14 | }); 15 | -------------------------------------------------------------------------------- /examples/block-extension/instant.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "previewStoreId": "${previewStoreId}", 3 | "organization": "acme-corporation", 4 | "blocks": { 5 | "CustomBlock": { 6 | "id": "2a1beddf-74ec-45d7-b0a6-da29f43dc8fb" 7 | }, 8 | "HeroBlock": { 9 | "id": "a3463dec-48d4-442c-aab0-6d2657a9010a" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/client/src/components/Link.tsx: -------------------------------------------------------------------------------- 1 | import { HTMLProps, ReactNode } from 'react'; 2 | import { createRemoteReactComponent } from '@remote-ui/react'; 3 | 4 | export interface LinkProps extends HTMLProps { 5 | children: ReactNode; 6 | to: any; 7 | } 8 | 9 | export const Link = createRemoteReactComponent<'Link', LinkProps>('Link'); 10 | -------------------------------------------------------------------------------- /packages/cli/src/lib/getBlockFiles.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import glob from 'glob'; 3 | import { dirname } from '~/config'; 4 | import { getBlockNameFromPath } from './getBlockNameFromPath'; 5 | 6 | export const getBlockFiles = () => 7 | glob 8 | .sync(path.join(dirname, 'src/blocks/**/index.tsx')) 9 | .map((file) => getBlockNameFromPath(file)); 10 | -------------------------------------------------------------------------------- /packages/client/src/BlockProvider/useBlockContext.ts: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | 3 | import { BlockContext } from './context'; 4 | 5 | export function useBlockContext() { 6 | const context = useContext(BlockContext); 7 | 8 | if (!context) { 9 | throw new Error('Expected block context not found'); 10 | } 11 | 12 | return context; 13 | } 14 | -------------------------------------------------------------------------------- /packages/preview/src/components/BlocksProvider/useBlocks.ts: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | 3 | import { BlocksContext } from './context'; 4 | 5 | export function useBlocks() { 6 | const context = useContext(BlocksContext); 7 | 8 | if (!context) { 9 | throw new Error('Expected blocks context not found'); 10 | } 11 | 12 | return context; 13 | } 14 | -------------------------------------------------------------------------------- /packages/preview/src/components/ConfigProvider/useConfig.tsx: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | 3 | import { ConfigContext } from './context'; 4 | 5 | export function useConfig() { 6 | const context = useContext(ConfigContext); 7 | 8 | if (!context) { 9 | throw new Error('Expected config context not found'); 10 | } 11 | 12 | return context; 13 | } 14 | -------------------------------------------------------------------------------- /templates/ts-tailwind/_gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /examples/block-extension/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /packages/cli/src/test.tsx: -------------------------------------------------------------------------------- 1 | /** @TODO not working yet */ 2 | import React from "react"; 3 | import chalk from "chalk"; 4 | import test from "ava"; 5 | import { render } from "ink-testing-library"; 6 | import { Logout } from "./commands"; 7 | 8 | test("Logout of current account", (t) => { 9 | const { lastFrame } = render(); 10 | 11 | t.is(lastFrame(), chalk`Successfully logged out.`); 12 | }); 13 | -------------------------------------------------------------------------------- /packages/cli/src/components/SelectInput/Item.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import type { FC } from "react"; 3 | import { Text } from "ink"; 4 | 5 | export interface Props { 6 | isSelected?: boolean; 7 | label: string; 8 | } 9 | 10 | const Item: FC = ({ isSelected = false, label }) => ( 11 | {label} 12 | ); 13 | 14 | export default Item; 15 | -------------------------------------------------------------------------------- /packages/vite-plugin/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tsup'; 2 | 3 | export default defineConfig({ 4 | entry: ['src/index.ts'], 5 | splitting: false, 6 | clean: true, 7 | dts: false, 8 | format: ['esm', 'cjs'], 9 | /** Fix for postcss plugin (which has to be cjs) */ 10 | banner: { 11 | js: "import { createRequire } from 'module';const require = createRequire(import.meta.url);", 12 | }, 13 | }); 14 | -------------------------------------------------------------------------------- /packages/cli/src/components/SelectInput/Indicator.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import type { FC } from "react"; 3 | import { Box, Text } from "ink"; 4 | 5 | export interface Props { 6 | isSelected?: boolean; 7 | } 8 | 9 | const Indicator: FC = ({ isSelected = false }) => ( 10 | {isSelected ? > : } 11 | ); 12 | 13 | export default Indicator; 14 | -------------------------------------------------------------------------------- /packages/preview/src/components/Input/Label.tsx: -------------------------------------------------------------------------------- 1 | import * as LabelPrimitive from '@radix-ui/react-label'; 2 | import { twMerge } from 'tailwind-merge'; 3 | 4 | export const Label = ({ 5 | className, 6 | children, 7 | ...props 8 | }: LabelPrimitive.LabelProps) => ( 9 | 13 | {children} 14 | 15 | ); 16 | -------------------------------------------------------------------------------- /packages/preview/src/containers/App.tsx: -------------------------------------------------------------------------------- 1 | import { Toaster } from 'react-hot-toast'; 2 | import { 3 | BlocksProvider, 4 | Layout, 5 | PreviewFrame, 6 | ConfigProvider, 7 | } from '../components'; 8 | 9 | export const App = () => { 10 | return ( 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | ); 21 | }; 22 | -------------------------------------------------------------------------------- /packages/preview/preview.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | DEV: Instant Block Extensions 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/preview/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import { BrowserRouter, Route, Routes } from 'react-router-dom'; 4 | 5 | import { App } from './containers'; 6 | import './index.css'; 7 | 8 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( 9 | 10 | 11 | 12 | } /> 13 | 14 | 15 | , 16 | ); 17 | -------------------------------------------------------------------------------- /packages/client/src/BlockProvider/index.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode, useMemo } from 'react'; 2 | 3 | import { BlockContext, BlockContextValue } from './context'; 4 | 5 | export * from './useBlockContext'; 6 | 7 | export const BlockProvider = ({ 8 | children, 9 | blockProps, 10 | }: { 11 | children: ReactNode; 12 | blockProps: BlockContextValue; 13 | }) => { 14 | const contextValue = useMemo(() => blockProps, [blockProps]); 15 | 16 | return ( 17 | 18 | {children} 19 | 20 | ); 21 | }; 22 | -------------------------------------------------------------------------------- /packages/preview/src/shims.d.ts: -------------------------------------------------------------------------------- 1 | declare interface Window { 2 | // extend the window 3 | __INSTANT_BLOCK_SERVER__: string; 4 | __INSTANT_BLOCKS_MANIFEST__: Record< 5 | string, 6 | { 7 | name: string; 8 | contentSchema?: any; 9 | customizationSchema?: any; 10 | } 11 | >; 12 | __INSTANT_STORES__?: Array<{ 13 | id: string; 14 | name: string; 15 | hostname: string; 16 | }>; 17 | __INITIAL_USER_CONFIG__?: { 18 | leftPanel: boolean; 19 | rightPanel: boolean; 20 | darkMode: boolean; 21 | scale: number; 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /packages/client/src/gql.ts: -------------------------------------------------------------------------------- 1 | import { gql as gqlTag } from 'graphql-tag'; 2 | 3 | /** 4 | * Use this homebaked gql implementation in prod instead of graphql-tag, 5 | * to reduce bundlesize of worker especially (±35kb). 6 | */ 7 | export const gql = 8 | process.env.NODE_ENV === 'development' 9 | ? gqlTag 10 | : (s: TemplateStringsArray, ...args: any[]) => 11 | s 12 | .map((ss, i) => `${ss}${args[i] || ''}`) 13 | .join('') 14 | .replace(/\s+#.*$/gm, '') // Remove GQL comments 15 | .replace(/\s+/gm, ' ') // Minify spaces 16 | .trim(); 17 | -------------------------------------------------------------------------------- /packages/cli/src/commands/logout.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render, Text } from "ink"; 3 | import { config } from "~/config"; 4 | import { CommandModule } from "yargs"; 5 | 6 | export const Logout = () => { 7 | config.delete("accessToken"); 8 | config.delete("refreshToken"); 9 | config.delete("organization"); 10 | config.delete("storeId"); 11 | 12 | return Successfully logged out.; 13 | }; 14 | 15 | export const logout: CommandModule = { 16 | command: "logout", 17 | describe: "Logout of current account", 18 | handler: () => { 19 | render(); 20 | }, 21 | }; 22 | -------------------------------------------------------------------------------- /packages/cli/src/cli.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import yargs from "yargs"; 3 | import { hideBin } from "yargs/helpers"; 4 | 5 | import "./env"; 6 | import * as commands from "./commands"; 7 | 8 | yargs(hideBin(process.argv)) 9 | .command(commands.login) 10 | .command(commands.whoami) 11 | .command(commands.logout) 12 | .command(commands.refresh) 13 | .command(commands.select) 14 | .command(commands.init) 15 | .command(commands.generate) 16 | .command(commands.dev) 17 | .command(commands.add) 18 | .command(commands.publish) 19 | .showHelpOnFail(true) 20 | .demandCommand() 21 | .recommendCommands() 22 | .strict().argv; 23 | -------------------------------------------------------------------------------- /packages/cli/src/lib/getProjectConfig.ts: -------------------------------------------------------------------------------- 1 | import Conf from 'conf'; 2 | 3 | interface ProjectConfigData { 4 | organization?: string; 5 | blocks: Record; 6 | } 7 | 8 | export const getProjectConfig = (path: string) => 9 | new Conf({ 10 | cwd: path, 11 | configName: 'instant.config', 12 | schema: { 13 | organization: { type: 'string' }, 14 | blocks: { 15 | type: 'object', 16 | additionalProperties: { 17 | type: 'object', 18 | properties: { 19 | id: { type: 'string' }, 20 | }, 21 | }, 22 | }, 23 | }, 24 | }); 25 | -------------------------------------------------------------------------------- /templates/ts-tailwind/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript-tailwind-starter", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "instant dev", 8 | "publish": "instant publish" 9 | }, 10 | "dependencies": { 11 | "@instantcommerce/sdk": "*", 12 | "react": "18.2.0", 13 | "react-dom": "18.2.0" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^18.0.17", 17 | "@types/react-dom": "^18.0.6", 18 | "autoprefixer": "^10.4.13", 19 | "postcss": "^8.4.18", 20 | "tailwindcss": "^3.2.2", 21 | "typescript": "^4.6.4", 22 | "vite": "^3.1.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /templates/ts-tailwind/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["src"], 20 | "references": [{ "path": "./tsconfig.node.json" }] 21 | } 22 | -------------------------------------------------------------------------------- /packages/preview/src/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './BlocksProvider'; 2 | export * from './Button'; 3 | export * from './ConfigProvider'; 4 | export * from './ColorInput'; 5 | export * from './ImageInput'; 6 | export * from './Input'; 7 | export * from './InputGroup'; 8 | export * from './Layout'; 9 | export * from './Modal'; 10 | export * from './PreviewFrame'; 11 | export * from './PreviewWrapper'; 12 | export * from './Resizable'; 13 | export * from './RichText'; 14 | export * from './Select'; 15 | export * from './Slider'; 16 | export * from './StatusMessage'; 17 | export * from './Tabs'; 18 | export * from './Toggle'; 19 | export * from './ToggleGroup'; 20 | export * from './Tooltip'; 21 | -------------------------------------------------------------------------------- /packages/postcss-plugin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@instantcommerce/postcss-plugin-sdk", 3 | "version": "0.0.0", 4 | "author": "Instant Commerce B.V.", 5 | "license": "MIT", 6 | "files": [ 7 | "dist" 8 | ], 9 | "main": "./dist/index.js", 10 | "types": "./dist/index.d.ts", 11 | "scripts": { 12 | "build": "tsup", 13 | "format": "prettier --write ." 14 | }, 15 | "engines": { 16 | "node": ">=16.0.0" 17 | }, 18 | "publishConfig": { 19 | "access": "public" 20 | }, 21 | "devDependencies": { 22 | "postcss": "8.4.18", 23 | "tsup": "6.5.0" 24 | }, 25 | "dependencies": { 26 | "postcss-selector-parser": "6.0.11" 27 | }, 28 | "peerDependencies": {} 29 | } 30 | -------------------------------------------------------------------------------- /packages/cli/src/config.ts: -------------------------------------------------------------------------------- 1 | import Conf from "conf"; 2 | import { fileURLToPath } from "url"; 3 | 4 | interface ConfigData { 5 | accessToken?: string; 6 | refreshToken?: string; 7 | organization?: string; 8 | storeId?: string; 9 | } 10 | 11 | export const config = new Conf({ 12 | projectName: "instant-cli", 13 | schema: { 14 | accessToken: { type: "string" }, 15 | refreshToken: { type: "string" }, 16 | organization: { type: "string" }, 17 | storeId: { type: "string" }, 18 | }, 19 | }); 20 | 21 | const resolvePath = (path: string) => 22 | fileURLToPath(new URL(path, import.meta.url)); 23 | 24 | export const dirname = (resolvePath(process.env["FORCE_DIR"] || process.cwd())); 25 | -------------------------------------------------------------------------------- /packages/cli/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@sindresorhus/tsconfig", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "strict": true, 6 | "module": "ESNext", 7 | "target": "ESNext", 8 | "lib": ["ESNext"], 9 | "moduleResolution": "node", 10 | "jsx": "react", 11 | "esModuleInterop": true, 12 | "incremental": true, 13 | "skipLibCheck": true, 14 | "resolveJsonModule": true, 15 | "noUnusedLocals": true, 16 | "strictNullChecks": true, 17 | "allowSyntheticDefaultImports": true, 18 | "outDir": "dist", 19 | "paths": { 20 | "~/*": ["src/*"] 21 | } 22 | }, 23 | "include": ["**/*.ts", "**/*.tsx"], 24 | "exclude": ["dist", "node_modules"] 25 | } 26 | -------------------------------------------------------------------------------- /packages/preview/src/components/InputGroup/index.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from 'react'; 2 | import { twMerge } from 'tailwind-merge'; 3 | 4 | interface InputGroupProps { 5 | title: string; 6 | children: ReactNode; 7 | className?: string; 8 | wrapperClassName?: string; 9 | } 10 | 11 | export const InputGroup = ({ 12 | title, 13 | children, 14 | className, 15 | wrapperClassName, 16 | }: InputGroupProps) => ( 17 |
18 |
19 | {title} 20 |
21 | 22 |
23 | {children} 24 |
25 |
26 | ); 27 | -------------------------------------------------------------------------------- /templates/ts-tailwind/src/blocks/HeroBlock/index.tsx: -------------------------------------------------------------------------------- 1 | import { defineBlock, useBlockState } from '@instantcommerce/sdk'; 2 | import './styles.css'; 3 | 4 | const HeroBlock = () => { 5 | const { content } = useBlockState(); 6 | 7 | return ( 8 |
9 |

{content.title}

10 |
11 | ); 12 | }; 13 | 14 | export default defineBlock({ 15 | component: HeroBlock, 16 | customizerSchema: { 17 | fields: { 18 | color: { type: 'color', label: 'Test color' }, 19 | }, 20 | }, 21 | contentSchema: { 22 | fields: { 23 | title: { type: 'text', label: 'Title', preview: 'Hero title' }, 24 | }, 25 | }, 26 | }); 27 | -------------------------------------------------------------------------------- /examples/block-extension/src/blocks/HeroBlock/index.tsx: -------------------------------------------------------------------------------- 1 | import { defineBlock, useBlockState } from '@instantcommerce/sdk'; 2 | import './index.css'; 3 | 4 | const Hero = () => { 5 | const { content } = useBlockState(); 6 | 7 | return ( 8 |
9 |

{content.title}

10 |
11 | ); 12 | }; 13 | 14 | const HeroBlock = defineBlock({ 15 | component: Hero, 16 | customizerSchema: { 17 | fields: { 18 | color: { type: 'text' }, 19 | }, 20 | }, 21 | contentSchema: { 22 | fields: { 23 | title: { type: 'text', label: 'Title', preview: 'Hero title' }, 24 | }, 25 | }, 26 | }); 27 | 28 | export default HeroBlock; 29 | -------------------------------------------------------------------------------- /packages/cli/src/lib/schemaFieldTypeMapping.ts: -------------------------------------------------------------------------------- 1 | import { 2 | DefineContentSchema, 3 | DefineCustomizerSchema, 4 | SchemaFieldType, 5 | } from 'types/schemas'; 6 | 7 | export const schemaFieldTypeMapping: Record< 8 | | DefineContentSchema['fields'][number]['type'] 9 | | DefineCustomizerSchema['fields'][number]['type'], 10 | SchemaFieldType 11 | > = { 12 | color: SchemaFieldType.COLOR, 13 | date: SchemaFieldType.DATE, 14 | image: SchemaFieldType.IMAGE, 15 | link: SchemaFieldType.LINK, 16 | number: SchemaFieldType.NUMBER, 17 | richText: SchemaFieldType.RICH_TEXT, 18 | select: SchemaFieldType.SELECT, 19 | subschema: SchemaFieldType.SUBSCHEMA, 20 | text: SchemaFieldType.TEXT, 21 | toggle: SchemaFieldType.TOGGLE, 22 | }; 23 | -------------------------------------------------------------------------------- /packages/preview/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "paths": { 19 | "instant-client/*": ["../client/src/*"] 20 | } 21 | }, 22 | "include": ["src"], 23 | "references": [{ "path": "./tsconfig.node.json" }] 24 | } 25 | -------------------------------------------------------------------------------- /packages/cli/src/templates/block.ts: -------------------------------------------------------------------------------- 1 | export const blockTemplate = ( 2 | name: string, 3 | ) => `import { defineBlock, useBlockState } from "@instantcommerce/sdk"; 4 | 5 | const ${name} = () => { 6 | const { content, customizer } = useBlockState(); 7 | 8 | return ( 9 |
10 |

11 | {content.title} 12 |

13 |
14 | ); 15 | }; 16 | 17 | export default defineBlock({ 18 | component: ${name}, 19 | customizerSchema: { 20 | fields: { 21 | color: { type: "color", label: "Color" }, 22 | }, 23 | }, 24 | contentSchema: { 25 | fields: { 26 | title: { type: "text", label: "Title", preview: 'Hero title' }, 27 | }, 28 | }, 29 | }); 30 | ; 31 | `; 32 | -------------------------------------------------------------------------------- /turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://turborepo.org/schema.json", 3 | "pipeline": { 4 | "build": { 5 | "dependsOn": ["^build"] 6 | }, 7 | "dev": { 8 | "cache": false 9 | }, 10 | "@instantcommerce/cli#build": { 11 | "dependsOn": [ 12 | "@instantcommerce/postcss-plugin-sdk#build", 13 | "@instantcommerce/vite-plugin-sdk#build", 14 | "@instantcommerce/sdk-preview#build" 15 | ] 16 | }, 17 | "@instantcommerce/cli#dev": { 18 | "dependsOn": [ 19 | "@instantcommerce/postcss-plugin-sdk#build", 20 | "@instantcommerce/vite-plugin-sdk#build", 21 | "@instantcommerce/sdk-preview#build", 22 | "@instantcommerce/sdk#build" 23 | ] 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/preview/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | DEV: Instant Block Extensions 8 | 9 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /examples/block-extension/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "paths": { 19 | "@instantcommerce/sdk/*": ["../packages/client/src/*"] 20 | } 21 | }, 22 | "include": ["src"], 23 | "references": [{ "path": "./tsconfig.node.json" }] 24 | } 25 | -------------------------------------------------------------------------------- /.graphqlrc.yml: -------------------------------------------------------------------------------- 1 | projects: 2 | default: 3 | schema: 4 | - http://localhost:4000/graphql 5 | documents: "./packages/cli/src/lib/api/**/*.graphql" 6 | extensions: 7 | codegen: 8 | generates: 9 | ./packages/cli/src/lib/api/schema.json: 10 | plugins: 11 | - introspection 12 | config: 13 | minify: true 14 | ./packages/types/api.ts: 15 | plugins: 16 | - typescript 17 | - typescript-operations 18 | ./packages/cli/src/lib/api/sdk.ts: 19 | plugins: 20 | - typescript 21 | - typescript-operations 22 | - typescript-graphql-request 23 | config: 24 | useTypeImports: true 25 | -------------------------------------------------------------------------------- /packages/preview/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { resolve } from 'path'; 2 | import react from '@vitejs/plugin-react'; 3 | import { defineConfig } from 'vite'; 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [react()], 8 | server: { 9 | port: 8080, 10 | }, 11 | build: { 12 | rollupOptions: { 13 | input: { 14 | main: resolve(__dirname, 'index.html'), 15 | preview: resolve(__dirname, 'preview.html'), 16 | }, 17 | }, 18 | }, 19 | resolve: { 20 | alias: { 21 | '@/': `${resolve(__dirname, '../ui/src')}/`, 22 | }, 23 | }, 24 | legacy: { 25 | buildSsrCjsExternalHeuristics: true, 26 | }, 27 | optimizeDeps: { 28 | include: [ 29 | '@remote-ui/core', 30 | '@remote-ui/react', 31 | '@shopify/web-worker/worker', 32 | ], 33 | }, 34 | }); 35 | -------------------------------------------------------------------------------- /packages/cli/src/lib/auth0/getDeviceCode.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { Auth0DeviceCodeResponse } from './abstractions'; 3 | import { AUTH0_DOMAIN, AUTH0_CLIENT_ID } from './auth0.constants'; 4 | 5 | /** 6 | * Get device code with verification details from Auth0. 7 | * 8 | * @returns Device code response 9 | */ 10 | export const getDeviceCode = async (): Promise => { 11 | const res = await axios.post( 12 | `https://${AUTH0_DOMAIN}/oauth/device/code`, 13 | new URLSearchParams({ 14 | client_id: AUTH0_CLIENT_ID, 15 | audience: `https://${AUTH0_DOMAIN}/api/v2/`, 16 | scope: 'offline_access', 17 | }), 18 | { 19 | headers: { 20 | 'accept-encoding': 'identity', 21 | 'content-type': 'application/x-www-form-urlencoded', 22 | }, 23 | }, 24 | ); 25 | 26 | return res.data; 27 | }; 28 | -------------------------------------------------------------------------------- /packages/tsconfig/base.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "Default", 4 | "compilerOptions": { 5 | "target": "ES2019", 6 | "module": "ESNext", 7 | "lib": ["ESNext", "DOM"], 8 | "esModuleInterop": true, 9 | "allowJs": true, 10 | "noImplicitAny": false, 11 | "checkJs": false, 12 | "strict": true, 13 | "strictNullChecks": true, 14 | "moduleResolution": "Node", 15 | "resolveJsonModule": true, 16 | "skipLibCheck": true, 17 | "sourceMap": false, 18 | "declaration": true, 19 | "declarationMap": false, 20 | "stripInternal": true, 21 | "allowSyntheticDefaultImports": true, 22 | "forceConsistentCasingInFileNames": true, 23 | "noFallthroughCasesInSwitch": false, 24 | "removeComments": false, 25 | "noEmit": true 26 | }, 27 | "exclude": ["node_modules"] 28 | } 29 | -------------------------------------------------------------------------------- /packages/preview/src/components/Resizable/Handle.tsx: -------------------------------------------------------------------------------- 1 | import { HTMLProps } from 'react'; 2 | import { twMerge } from 'tailwind-merge'; 3 | 4 | interface HandleProps extends HTMLProps { 5 | position: 'top' | 'right' | 'bottom' | 'left'; 6 | } 7 | 8 | export const Handle = ({ position, className, ...props }: HandleProps) => ( 9 |
13 |
29 |
30 | ); 31 | -------------------------------------------------------------------------------- /packages/cli/src/lib/auth0/refreshAccessToken.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { Auth0AccessTokenResponse } from './abstractions'; 3 | import { AUTH0_DOMAIN, AUTH0_CLIENT_ID } from './auth0.constants'; 4 | 5 | /** 6 | * Get new tokens from Auth0. 7 | * 8 | * @param refreshToken The current refresh token 9 | * @returns Access token response 10 | */ 11 | export const refreshAccessToken = async ( 12 | refreshToken: string, 13 | ): Promise => { 14 | const res = await axios.post( 15 | `https://${AUTH0_DOMAIN}/oauth/token`, 16 | new URLSearchParams({ 17 | grant_type: 'refresh_token', 18 | refresh_token: refreshToken, 19 | client_id: AUTH0_CLIENT_ID, 20 | }), 21 | { 22 | headers: { 23 | 'accept-encoding': 'identity', 24 | 'content-type': 'application/x-www-form-urlencoded', 25 | }, 26 | }, 27 | ); 28 | 29 | return res.data; 30 | }; 31 | -------------------------------------------------------------------------------- /packages/cli/src/lib/getViteConfig.ts: -------------------------------------------------------------------------------- 1 | import instantSdk from '@instantcommerce/vite-plugin-sdk'; 2 | import react from '@vitejs/plugin-react'; 3 | import { InlineConfig, mergeConfig } from 'vite'; 4 | import { dirname } from '~/config'; 5 | 6 | export const getViteConfig = async ( 7 | mode = 'development', 8 | overrides?: Partial, 9 | blockIdsMap?: Record>, 10 | entry?: string, 11 | ) => { 12 | return mergeConfig( 13 | { 14 | configFile: `${dirname}/vite.config.ts`, 15 | root: dirname, 16 | plugins: [ 17 | react({ 18 | fastRefresh: false, 19 | /** @todo investigate */ 20 | jsxRuntime: mode === 'development' ? 'automatic' : 'classic', 21 | }), 22 | instantSdk({ 23 | blockIdsMap, 24 | entry, 25 | }) as any, 26 | ], 27 | } as InlineConfig, 28 | overrides || {}, 29 | ); 30 | }; 31 | -------------------------------------------------------------------------------- /packages/cli/src/lib/auth0/checkDeviceCode.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { Auth0AccessTokenResponse } from './abstractions'; 3 | import { AUTH0_DOMAIN, AUTH0_CLIENT_ID } from './auth0.constants'; 4 | 5 | /** 6 | * Checks if given device code has been verified in Auth0. 7 | * 8 | * @param deviceCode The device code to validate 9 | * @returns Access token response 10 | */ 11 | export const checkDeviceCode = async ( 12 | deviceCode: string, 13 | ): Promise => { 14 | const res = await axios.post( 15 | `https://${AUTH0_DOMAIN}/oauth/token`, 16 | new URLSearchParams({ 17 | grant_type: 'urn:ietf:params:oauth:grant-type:device_code', 18 | device_code: deviceCode, 19 | client_id: AUTH0_CLIENT_ID, 20 | }), 21 | { 22 | headers: { 23 | 'accept-encoding': 'identity', 24 | 'content-type': 'application/x-www-form-urlencoded', 25 | }, 26 | }, 27 | ); 28 | 29 | return res.data; 30 | }; 31 | -------------------------------------------------------------------------------- /packages/preview/src/containers/Preview/components.tsx: -------------------------------------------------------------------------------- 1 | import { LinkProps, RichTextProps } from '@instantcommerce/sdk'; 2 | import { render } from 'storyblok-rich-text-react-renderer'; 3 | 4 | export const Link = ({ children, to, ...props }: LinkProps) => { 5 | return ( 6 | { 10 | e.preventDefault(); 11 | }} 12 | > 13 | {children} 14 | 15 | ); 16 | }; 17 | 18 | export const RichText = ({ 19 | value, 20 | blokResolvers, 21 | defaultBlokResolver, 22 | markResolvers, 23 | nodeResolvers, 24 | defaultStringResolver, 25 | textResolver, 26 | ...props 27 | }: RichTextProps) => { 28 | return ( 29 |
30 | {render(value, { 31 | blokResolvers, 32 | defaultBlokResolver, 33 | markResolvers, 34 | nodeResolvers, 35 | defaultStringResolver, 36 | textResolver, 37 | })} 38 |
39 | ); 40 | }; 41 | -------------------------------------------------------------------------------- /packages/preview/src/containers/Preview/storeFallback.ts: -------------------------------------------------------------------------------- 1 | import { PublicStore } from 'types/api'; 2 | 3 | export const storeFallback = { 4 | storefront: { 5 | config: { 6 | blocks: [ 7 | { 8 | __typename: 'BlockquoteBlock', 9 | id: '123', 10 | // authorColor: '#000', 11 | // backgroundColor: 'yellow', 12 | // blockBackgroundColor: 'pink', 13 | blockBorderRadius: 'MEDIUM', 14 | contentAlignment: 'LEFT', 15 | hasLeftBorder: true, 16 | // leftBorderColor: 'purple', 17 | name: '123', 18 | textSize: 'MEDIUM', 19 | theme: 'PRIMARY', 20 | } as any, 21 | ], 22 | components: { 23 | button: { 24 | borderRadius: 'SMALL', 25 | boxShadow: 'LARGE', 26 | fontWeight: 'MEDIUM', 27 | }, 28 | input: { 29 | borderRadius: 'SMALL', 30 | }, 31 | }, 32 | }, 33 | }, 34 | } as any as PublicStore; 35 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Semantic Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - beta 7 | - main 8 | 9 | jobs: 10 | release: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout repo 15 | uses: actions/checkout@v3 16 | with: 17 | persist-credentials: false 18 | 19 | - name: Setup Node.js 20 | uses: actions/setup-node@v3 21 | with: 22 | node-version: 18 23 | cache: "yarn" 24 | 25 | - name: Install dependencies 26 | run: yarn 27 | 28 | - name: Build 29 | run: yarn build 30 | 31 | - name: Install semantic-release 32 | run: yarn global add semantic-release semantic-release-monorepo @semantic-release/changelog @semantic-release/npm 33 | 34 | - name: Release 35 | run: yarn workspaces run semantic-release -e semantic-release-monorepo 36 | env: 37 | GITHUB_TOKEN: ${{ secrets.GH_RELEASE_TOKEN }} 38 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 39 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.watcherExclude": { 3 | "**/.cache/**": true, 4 | "**/dist": true 5 | }, 6 | "search.exclude": { 7 | "**/dist": true 8 | }, 9 | "files.exclude": { 10 | "**/.DS_Store": true 11 | }, 12 | "editor.formatOnSave": true, 13 | // turn it off for JS and JSX, we will do this via eslint 14 | "[javascript]": { 15 | "editor.formatOnSave": false, 16 | "editor.defaultFormatter": null 17 | }, 18 | "[javascriptreact]": { 19 | "editor.formatOnSave": false, 20 | "editor.defaultFormatter": null 21 | }, 22 | "[typescript]": { 23 | "editor.formatOnSave": false, 24 | "editor.defaultFormatter": null 25 | }, 26 | "[typescriptreact]": { 27 | "editor.formatOnSave": false, 28 | "editor.defaultFormatter": null 29 | }, 30 | // tell the ESLint plugin to run on save 31 | "editor.codeActionsOnSave": { 32 | "source.fixAll": true 33 | }, 34 | "editor.tabSize": 2, 35 | "editor.insertSpaces": true, 36 | "editor.defaultFormatter": "esbenp.prettier-vscode" 37 | } 38 | -------------------------------------------------------------------------------- /packages/vite-plugin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@instantcommerce/vite-plugin-sdk", 3 | "version": "0.0.0", 4 | "author": "Instant Commerce B.V.", 5 | "license": "MIT", 6 | "files": [ 7 | "dist" 8 | ], 9 | "type": "module", 10 | "main": "./dist/index.cjs", 11 | "module": "./dist/index.js", 12 | "types": "./dist/index.d.ts", 13 | "exports": { 14 | ".": { 15 | "types": "./dist/index.d.ts", 16 | "require": "./dist/index.cjs", 17 | "import": "./dist/index.js" 18 | } 19 | }, 20 | "publishConfig": { 21 | "access": "public" 22 | }, 23 | "scripts": { 24 | "build": "tsup", 25 | "format": "prettier --write ." 26 | }, 27 | "engines": { 28 | "node": ">=16.0.0" 29 | }, 30 | "dependencies": { 31 | "@babel/core": "7.20.5", 32 | "@instantcommerce/postcss-plugin-sdk": "*", 33 | "glob": "8.0.3", 34 | "hashids": "2.2.10", 35 | "react-refresh": "^0.14.0" 36 | }, 37 | "devDependencies": { 38 | "tsconfig": "*", 39 | "tsup": "6.5.0" 40 | }, 41 | "peerDependencies": { 42 | "vite": "^3.0.0" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/client/src/components/RichText.tsx: -------------------------------------------------------------------------------- 1 | import { HTMLProps } from 'react'; 2 | import { createRemoteReactComponent } from '@remote-ui/react'; 3 | import { 4 | RenderOptions, 5 | NODE_HEADING, 6 | NODE_CODEBLOCK, 7 | NODE_PARAGRAPH, 8 | NODE_QUOTE, 9 | NODE_OL, 10 | NODE_UL, 11 | NODE_LI, 12 | NODE_HR, 13 | NODE_BR, 14 | NODE_IMAGE, 15 | MARK_BOLD, 16 | MARK_ITALIC, 17 | MARK_STRIKE, 18 | MARK_UNDERLINE, 19 | MARK_CODE, 20 | MARK_LINK, 21 | MARK_STYLED, 22 | } from 'storyblok-rich-text-react-renderer'; 23 | 24 | export { 25 | NODE_HEADING, 26 | NODE_CODEBLOCK, 27 | NODE_PARAGRAPH, 28 | NODE_QUOTE, 29 | NODE_OL, 30 | NODE_UL, 31 | NODE_LI, 32 | NODE_HR, 33 | NODE_BR, 34 | NODE_IMAGE, 35 | MARK_BOLD, 36 | MARK_ITALIC, 37 | MARK_STRIKE, 38 | MARK_UNDERLINE, 39 | MARK_CODE, 40 | MARK_LINK, 41 | MARK_STYLED, 42 | }; 43 | 44 | export interface RichTextProps 45 | extends RenderOptions, 46 | HTMLProps { 47 | value: any; 48 | } 49 | 50 | export const RichText = createRemoteReactComponent<'RichText', RichTextProps>( 51 | 'RichText', 52 | ); 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | Instant Commerce 4 | 5 |

Instant Commerce SDK

6 |
7 | 8 |
9 |

10 | The Instant Commerce SDK enables developers to build completely custom React based blocks and publish them to the Instant Commerce platform. 11 |

12 | 13 | [View documentation 📚](https://docs.instantcommerce.io/) 14 | 15 |
16 | 17 | --- 18 | 19 | ## Installation ⚡ 20 | 21 | The CLI is available as an npm package. To install the package run: 22 | 23 | ```bash 24 | npm install -g @instantcommerce/cli 25 | ``` 26 | 27 | ## Getting started 28 | 29 | Read our [Getting Started guide](https://docs.instantcommerce.io/) to get up and running. 30 | 31 | ## Instant SDK examples 32 | 33 | The [instant-sdk-examples](https://github.com/instantcommerce/instant-sdk-examples) project contains a collection of helpful blocks and components built with the Instant SDK. 34 | -------------------------------------------------------------------------------- /.lintstagedrc.js: -------------------------------------------------------------------------------- 1 | const ESLint = require('eslint').ESLint 2 | 3 | const removeIgnoredFiles = async (files) => { 4 | const eslint = new ESLint() 5 | const isIgnored = await Promise.all( 6 | files.map((file) => { 7 | return eslint.isPathIgnored(file) 8 | }) 9 | ) 10 | const filteredFiles = files.filter((_, i) => !isIgnored[i]) 11 | return filteredFiles.join(' ') 12 | } 13 | 14 | module.exports = { 15 | "dashboard/**/*.{js,ts,jsx,tsx}": async (files) => { 16 | const filesToLint = await removeIgnoredFiles(files) 17 | return [`eslint --max-warnings=0 ${filesToLint} --fix`] 18 | }, 19 | "storefront/**/*.{js,ts,jsx,tsx}": async (files) => { 20 | const filesToLint = await removeIgnoredFiles(files) 21 | return [`eslint --max-warnings=0 ${filesToLint} --fix`] 22 | }, 23 | "packages/**/**/*.{js,ts,jsx,tsx}": async (files) => { 24 | const filesToLint = await removeIgnoredFiles(files) 25 | return [`eslint --max-warnings=0 ${filesToLint} --fix`] 26 | }, 27 | "*.json": async (files) => { 28 | const filesToLint = await removeIgnoredFiles(files) 29 | return [`prettier --write ${filesToLint} --fix`] 30 | }, 31 | } 32 | -------------------------------------------------------------------------------- /packages/cli/README.md: -------------------------------------------------------------------------------- 1 | # @instantcommerce/cli 2 | 3 | ## Install 4 | 5 | ```bash 6 | $ npm install --global @instantcommerce/cli 7 | ``` 8 | 9 | ## CLI 10 | 11 | ``` 12 | $ instant --help 13 | 14 | Usage 15 | $ instant 16 | 17 | Commands: 18 | instant login Login to your account 19 | instant whoami Check current login status 20 | instant logout Logout of current account 21 | instant refresh Refresh your access token 22 | instant select Select organization and store 23 | instant init