Skip to content
Merged
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
8 changes: 7 additions & 1 deletion packages/cli-tools/src/constants/deploy.constants.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import type {Precompress} from '@junobuild/config';

export const DEPLOY_DEFAULT_SOURCE = 'build';
export const DEPLOY_DEFAULT_IGNORE = [];
export const DEPLOY_DEFAULT_ENCODING = [];
export const DEPLOY_DEFAULT_GZIP = '**/*.+(css|js|mjs|html)';
export const DEPLOY_DEFAULT_PRECOMPRESS: Required<Precompress> = {
pattern: '**/*.+(css|js|mjs|html)',
mode: 'both',
algorithm: 'gzip'
};

export const IGNORE_OS_FILES = ['.ds_store', 'thumbs.db'];
export const UPLOAD_BATCH_SIZE = 20;
Expand Down
12 changes: 6 additions & 6 deletions packages/cli-tools/src/services/deploy.prepare.services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import {readFile} from 'node:fs/promises';
import {extname, join} from 'node:path';
import {
DEPLOY_DEFAULT_ENCODING,
DEPLOY_DEFAULT_GZIP,
DEPLOY_DEFAULT_IGNORE,
DEPLOY_DEFAULT_PRECOMPRESS,
DEPLOY_DEFAULT_SOURCE
} from '../constants/deploy.constants';
import type {FileDetails, FileExtension, ListAssets, PrepareDeployOptions} from '../types/deploy';
Expand All @@ -33,7 +33,7 @@ export const prepareDeploy = async ({
source = DEPLOY_DEFAULT_SOURCE,
ignore = DEPLOY_DEFAULT_IGNORE,
encoding = DEPLOY_DEFAULT_ENCODING,
gzip = DEPLOY_DEFAULT_GZIP
precompress = DEPLOY_DEFAULT_PRECOMPRESS
} = config;

const sourceAbsolutePath = join(process.cwd(), source);
Expand All @@ -44,7 +44,7 @@ export const prepareDeploy = async ({
sourceAbsolutePath,
ignore,
encoding,
gzip,
precompress,
listAssets,
includeAllFiles
});
Expand Down Expand Up @@ -123,15 +123,15 @@ const listFiles = async ({
sourceAbsolutePath,
ignore,
encoding,
gzip,
precompress,
listAssets,
includeAllFiles
}: {
sourceAbsolutePath: string;
} & {listAssets: ListAssets} & Pick<PrepareDeployOptions, 'includeAllFiles'> &
Required<Pick<CliConfig, 'ignore' | 'encoding' | 'gzip'>>): Promise<FileDetails[]> => {
Required<Pick<CliConfig, 'ignore' | 'encoding' | 'precompress'>>): Promise<FileDetails[]> => {
const sourceFiles = listSourceFiles({sourceAbsolutePath, ignore});
const compressedFiles = await gzipFiles({sourceFiles, gzip});
const compressedFiles = await gzipFiles({sourceFiles, precompress});

const files = [...sourceFiles, ...compressedFiles.filter((file) => !sourceFiles.includes(file))];

Expand Down
15 changes: 9 additions & 6 deletions packages/cli-tools/src/utils/compress.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {minimatch} from 'minimatch';
import {createReadStream, createWriteStream} from 'node:fs';
import {Readable} from 'node:stream';
import {createGunzip, createGzip} from 'node:zlib';
import {DEPLOY_DEFAULT_GZIP} from '../constants/deploy.constants';
import {DEPLOY_DEFAULT_PRECOMPRESS} from '../constants/deploy.constants';

export const gunzipFile = async ({source}: {source: Buffer}): Promise<Buffer> =>
await new Promise<Buffer>((resolve, reject) => {
Expand All @@ -24,14 +24,17 @@ export const gunzipFile = async ({source}: {source: Buffer}): Promise<Buffer> =>

export const gzipFiles = async ({
sourceFiles,
gzip
}: {sourceFiles: string[]} & Required<Pick<SatelliteConfig, 'gzip'>>): Promise<string[]> => {
if (gzip === false) {
precompress
}: {sourceFiles: string[]} & Required<Pick<SatelliteConfig, 'precompress'>>): Promise<string[]> => {
if (precompress === false) {
return [];
}

// @ts-expect-error we read json so, it's possible that one provide a boolean that does not match the TS type
const pattern = gzip === true ? DEPLOY_DEFAULT_GZIP : gzip;
const pattern =
// @ts-expect-error we read json so, it's possible that one provide a boolean that does not match the TS type
precompress === true
? DEPLOY_DEFAULT_PRECOMPRESS.pattern
: (precompress.pattern ?? DEPLOY_DEFAULT_PRECOMPRESS.pattern);

const filesToCompress = sourceFiles.filter((file) => minimatch(file, pattern));
return await Promise.all(filesToCompress.map(async (source) => await gzipFile({source})));
Expand Down
54 changes: 41 additions & 13 deletions packages/config/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ Configuration options for [Juno] CLI.
- [StorageConfigRedirectSchema](#gear-storageconfigredirectschema)
- [StorageConfigSchema](#gear-storageconfigschema)
- [EncodingTypeSchema](#gear-encodingtypeschema)
- [PrecompressSchema](#gear-precompressschema)
- [CliConfigSchema](#gear-cliconfigschema)
- [SatelliteIdSchema](#gear-satelliteidschema)
- [SatelliteIdsSchema](#gear-satelliteidsschema)
Expand Down Expand Up @@ -368,17 +369,29 @@ see EncodingType

[:link: Source](https://github.com/junobuild/juno-js/tree/main/packages/config/src/types/encoding.ts#L6)

#### :gear: PrecompressSchema

| Constant | Type |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `PrecompressSchema` | `ZodObject<{ pattern: ZodOptional<ZodString>; mode: ZodOptional<ZodEnum<{ both: "both"; replace: "replace"; }>>; algorithm: ZodOptional<ZodEnum<{ gzip: "gzip"; }>>; }, $strict>` |

References:

- Precompress

[:link: Source](https://github.com/junobuild/juno-js/tree/main/packages/config/src/types/cli.config.ts#L7)

#### :gear: CliConfigSchema

| Constant | Type |
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `CliConfigSchema` | `ZodObject<{ source: ZodOptional<ZodString>; ignore: ZodOptional<ZodArray<ZodString>>; gzip: ZodOptional<ZodUnion<readonly [ZodString, ZodLiteral<...>]>>; encoding: ZodOptional<...>; predeploy: ZodOptional<...>; postdeploy: ZodOptional<...>; }, $strict>` |
| Constant | Type |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `CliConfigSchema` | `ZodObject<{ source: ZodOptional<ZodString>; ignore: ZodOptional<ZodArray<ZodString>>; precompress: ZodOptional<ZodUnion<readonly [ZodObject<{ pattern: ZodOptional<ZodString>; mode: ZodOptional<...>; algorithm: ZodOptional<...>; }, $strict>, ZodLiteral<...>]>>; encoding: ZodOptional<...>; predeploy: ZodOptional<...>;...` |

References:

- CliConfig

[:link: Source](https://github.com/junobuild/juno-js/tree/main/packages/config/src/types/cli.config.ts#L7)
[:link: Source](https://github.com/junobuild/juno-js/tree/main/packages/config/src/types/cli.config.ts#L16)

#### :gear: SatelliteIdSchema

Expand Down Expand Up @@ -439,6 +452,7 @@ References:
- [StorageConfigRewrite](#gear-storageconfigrewrite)
- [StorageConfigRedirect](#gear-storageconfigredirect)
- [StorageConfig](#gear-storageconfig)
- [Precompress](#gear-precompress)
- [CliConfig](#gear-cliconfig)
- [SatelliteId](#gear-satelliteid)
- [SatelliteIds](#gear-satelliteids)
Expand Down Expand Up @@ -698,18 +712,32 @@ Configures the hosting behavior of the Storage.

[:link: Source](https://github.com/junobuild/juno-js/tree/main/packages/config/src/shared/storage.config.ts#L128)

#### :gear: Precompress

Configuration for compressing files during deployment.

| Property | Type | Description |
| ----------- | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --- | --- | ---- |
| `pattern` | `string or undefined` | Glob pattern for files to precompress. default: any css | js | mjs | html |
| `mode` | `"both" or "replace" or undefined` | Determines what happens to the original files after compression: - `"both"` — upload both original and compressed versions. - `"replace"` — upload only the compressed version (served with `Content-Encoding`). default: "both" |
| `algorithm` | `"gzip" or undefined` | Compression algorithm. default: "gzip" |

[:link: Source](https://github.com/junobuild/juno-js/tree/main/packages/config/src/types/cli.config.ts#L28)

#### :gear: CliConfig

| Property | Type | Description |
| ------------ | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `source` | `string or undefined` | Specifies the directory from which to deploy to Storage. For instance, if `npm run build` outputs files to a `dist` folder, use `source: 'dist'`. default: 'build'type: {string} |
| `ignore` | `string[] or undefined` | Specifies files or patterns to ignore during deployment, using glob patterns similar to those in .gitignore. type: {string[]}optional |
| `gzip` | `string or false or undefined` | Controls the Gzip compression optimization for files in the source folder. By default, it targets JavaScript (js), ES Module (mjs), and CSS (css) files. You can disable this by setting it to `false` or customize it with a different file matching pattern using glob syntax. type: {string or false}optional |
| `encoding` | `[string, EncodingType][] or undefined` | Customizes file encoding mapping for HTTP response headers `Content-Encoding` based on file extension: - `.Z` for compress, - `.gz` for gzip, - `.br` for brotli, - `.zlib` for deflate, - anything else defaults to `identity`. The "encoding" attribute allows overriding default mappings with an array of glob patterns and encoding types. type: {Array<[string, EncodingType]>}optional |
| `predeploy` | `string[] or undefined` | Defines a list of scripts or commands to be run before the deployment process begins. This can be useful for tasks such as compiling assets, running tests, or building production-ready files. Example: `json { "predeploy": ["npm run build", "npm run lint"] } ` type: {string[]}optional |
| `postdeploy` | `string[] or undefined` | Defines a list of scripts or commands to be run after the deployment process completes. This can be used for tasks such as notifications, cleanup, or sending confirmation messages to services or team members. Example: `json { "postdeploy": ["./scripts/notify-admins.sh", "echo 'Deployment complete'"] } ` type: {string[]}optional |
The configuration used by the CLI to resolve, prepare and deploy your app.

| Property | Type | Description |
| ------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `source` | `string or undefined` | Specifies the directory from which to deploy to Storage. For instance, if `npm run build` outputs files to a `dist` folder, use `source: 'dist'`. default: 'build'type: {string} |
| `ignore` | `string[] or undefined` | Specifies files or patterns to ignore during deployment, using glob patterns similar to those in .gitignore. type: {string[]}optional |
| `precompress` | `false or Precompress or undefined` | Controls compression optimization for files in the source folder. By default, JavaScript (.js), ES Modules (.mjs), CSS (.css), and HTML (.html) are compressed, and both the original and compressed versions are uploaded. Set to `false` to disable, or provide a {@link Precompress} object to customize. type: {Precompress or false}optional |
| `encoding` | `[string, EncodingType][] or undefined` | Customizes file encoding mapping for HTTP response headers `Content-Encoding` based on file extension: - `.Z` for compress, - `.gz` for gzip, - `.br` for brotli, - `.zlib` for deflate, - anything else defaults to `identity`. The "encoding" attribute allows overriding default mappings with an array of glob patterns and encoding types. type: {Array<[string, EncodingType]>}optional |
| `predeploy` | `string[] or undefined` | Defines a list of scripts or commands to be run before the deployment process begins. This can be useful for tasks such as compiling assets, running tests, or building production-ready files. Example: `json { "predeploy": ["npm run build", "npm run lint"] } ` type: {string[]}optional |
| `postdeploy` | `string[] or undefined` | Defines a list of scripts or commands to be run after the deployment process completes. This can be used for tasks such as notifications, cleanup, or sending confirmation messages to services or team members. Example: `json { "postdeploy": ["./scripts/notify-admins.sh", "echo 'Deployment complete'"] } ` type: {string[]}optional |

[:link: Source](https://github.com/junobuild/juno-js/tree/main/packages/config/src/types/cli.config.ts#L18)
[:link: Source](https://github.com/junobuild/juno-js/tree/main/packages/config/src/types/cli.config.ts#L54)

#### :gear: SatelliteId

Expand Down
2 changes: 1 addition & 1 deletion packages/config/src/tests/console/console.config.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe('console.config', () => {
const result = JunoConsoleConfigSchema.safeParse({
id: mockModuleIdText,
source: 'dist',
gzip: '*.js',
precompress: false,
predeploy: ['npm run build'],
postdeploy: ['echo done']
});
Expand Down
49 changes: 45 additions & 4 deletions packages/config/src/tests/types/cli.config.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ describe('cli.config', () => {
const config: CliConfig = {
source: 'dist',
ignore: ['**/*.test.js'],
gzip: '*.js',
precompress: {
pattern: '**/*.+(css|js|mjs|html)',
mode: 'both',
algorithm: 'gzip'
},
encoding: [['*.br', 'br']],
predeploy: ['npm run build'],
postdeploy: ['echo done']
Expand All @@ -21,9 +25,46 @@ describe('cli.config', () => {
expect(result.success).toBe(true);
});

it('accepts gzip as false', () => {
const result = CliConfigSchema.safeParse({gzip: false});
expect(result.success).toBe(true);
describe('precompress', () => {
it('accepts precompress as false (opt-out)', () => {
const result = CliConfigSchema.safeParse({precompress: false});
expect(result.success).toBe(true);
});

it('accepts precompress mode replace', () => {
const result = CliConfigSchema.safeParse({
precompress: {
mode: 'replace'
}
});
expect(result.success).toBe(true);
});

it('rejects invalid precompress.mode', () => {
const result = CliConfigSchema.safeParse({
precompress: {mode: 'off'}
});
expect(result.success).toBe(false);
});

it('rejects invalid precompress.algorithm', () => {
const result = CliConfigSchema.safeParse({
precompress: {algorithm: 'br'}
});
expect(result.success).toBe(false);
});

it('rejects precompress if not an object or false', () => {
const result = CliConfigSchema.safeParse({precompress: 123});
expect(result.success).toBe(false);
});

it('rejects precompress.pattern if not a string', () => {
const result = CliConfigSchema.safeParse({
precompress: {pattern: 123}
});
expect(result.success).toBe(false);
});
});

it('rejects invalid encoding values', () => {
Expand Down
Loading