chore(pack-up): fail on any typing issue

This commit is contained in:
Alexandre Bodin 2024-01-17 13:50:23 +01:00
parent 316470cdf8
commit 1ffeb510a8
24 changed files with 123 additions and 98 deletions

View File

@ -31,7 +31,10 @@ export default ({ strapi }: { strapi: Strapi }) => ({
return this.findUniqueUID({
contentTypeUID,
field,
value: slugify(defaultValue || contentType.modelName, options),
value: slugify(
_.isFunction(defaultValue) ? defaultValue() : defaultValue || contentType.modelName,
options
),
});
},

View File

@ -178,6 +178,9 @@ class Strapi extends Container implements StrapiI {
features: FeaturesService;
// @ts-expect-error - Assigned in constructor
ee: StrapiI['ee'];
constructor(opts: StrapiOptions = {}) {
super();

View File

@ -1,15 +0,0 @@
import glob, { IOptions } from 'glob';
/**
* Promise based glob
*/
function promiseGlob(...args: [string, IOptions]): Promise<string[]> {
return new Promise((resolve, reject) => {
glob(...args, (err, files) => {
if (err) return reject(err);
resolve(files);
});
});
}
export default promiseGlob;

View File

@ -2,6 +2,7 @@ import type { Fetch, Strapi } from '@strapi/types';
import { ProxyAgent } from 'undici';
// TODO: once core Node exposes a stable way to create a ProxyAgent we will use that instead of undici
export type { Fetch };
// Create a wrapper for Node's Fetch API that applies a global proxy
export function createStrapiFetch(strapi: Strapi): Fetch {

View File

@ -28,7 +28,6 @@ export const transformContentTypesToModels = (
): DatabaseConfig['models'] => {
return contentTypes.map((contentType) => {
// Add document id to content types
// @ts-expect-error - `default` function is not typed into `Attribute`
// as it is not documented
const documentIdAttribute: Record<string, Attribute.Any> =
contentType.modelType === 'contentType'

View File

@ -1,3 +1,4 @@
import type { IncomingMessage } from 'node:http';
import { randomUUID } from 'crypto';
import type { Context } from 'koa';
import type { RawData, ServerOptions } from 'ws';
@ -9,7 +10,7 @@ import { ProviderError, ProviderTransferError } from '../../../errors/providers'
import { VALID_TRANSFER_COMMANDS, ValidTransferCommand } from './constants';
import { TransferMethod } from '../constants';
type WSCallback = (client: WebSocket, request: Response) => void;
type WSCallback = (client: WebSocket, request: IncomingMessage) => void;
export interface HandlerOptions {
verify: (ctx: Context, scope?: TransferMethod) => Promise<void>;

View File

@ -13,7 +13,7 @@ const { ApplicationError } = errors;
*/
const emailController = {
async send(ctx: Koa.Context) {
const options: SendOptions = ctx.request.body;
const options = ctx.request.body as SendOptions;
try {
await strapi.plugin('email').service('email').send(options);
@ -32,7 +32,7 @@ const emailController = {
},
async test(ctx: Koa.Context) {
const { to } = ctx.request.body;
const { to } = ctx.request.body as Pick<SendOptions, 'to'>;
if (!to) {
throw new ApplicationError('No recipient(s) are given');

View File

@ -1,7 +1,7 @@
{
"extends": "./server/tsconfig.json",
"extends": "./tsconfig.json",
"include": ["./src", "../shared"],
"exclude": ["./server/src/**/*.test.ts"],
"exclude": ["node_modules", "**/*.test.ts"],
"compilerOptions": {
"rootDir": "../",
"baseUrl": ".",

View File

@ -3,6 +3,6 @@
"compilerOptions": {
"noEmit": true
},
"include": ["src"],
"include": ["src", "../shared"],
"exclude": ["node_modules"]
}

View File

@ -3,7 +3,6 @@
"include": ["./src", "../shared"],
"compilerOptions": {
"rootDir": "../",
"baseUrl": ".",
"esModuleInterop": true
"baseUrl": "."
}
}

View File

@ -73,6 +73,7 @@
"@strapi/icons": "1.14.1",
"@strapi/pack-up": "4.17.1",
"@strapi/types": "4.17.1",
"@strapi/utils": "4.17.1",
"@testing-library/react": "14.0.0",
"@testing-library/user-event": "14.4.3",
"cross-env": "^7.0.3",

View File

@ -173,9 +173,12 @@
},
"devDependencies": {
"@strapi/pack-up": "workspace:*",
"@types/find-root": "1.1.4",
"@types/jest": "29.5.2",
"@types/lodash": "^4.14.191",
"@types/node": "18.18.4",
"@types/webpack-bundle-analyzer": "4.6.3",
"@types/webpack-hot-middleware": "2.25.9",
"eslint-config-custom": "4.17.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",

View File

@ -17,6 +17,8 @@ interface BaseOptions {
stats?: boolean;
minify?: boolean;
sourcemaps?: boolean;
bundler?: 'webpack' | 'vite';
open?: boolean;
}
interface BuildContext<TOptions = unknown> extends BaseContext {
@ -51,7 +53,7 @@ const DEFAULT_BROWSERSLIST = [
'not dead',
];
const createBuildContext = async <TOptions>({
const createBuildContext = async <TOptions extends BaseOptions>({
cwd,
logger,
tsconfig,

View File

@ -23,7 +23,6 @@ const watch = async (ctx: BuildContext): Promise<WebpackWatcher> => {
const devMiddleware = webpackDevMiddleware(compiler);
// @ts-expect-error ignored
const hotMiddleware = webpackHotMiddleware(compiler, {
log: false,
path: '/__webpack_hmr',

View File

@ -56,7 +56,7 @@ type GetPopulatableKeysWithoutTarget<TSchemaUID extends Common.UID.Schema> = Exc
* Fragment populate notation for polymorphic attributes
*/
export type Fragment<TMaybeTargets extends Common.UID.Schema> = {
on?: { [TSchemaUID in TMaybeTargets]?: boolean | NestedParams<TSchemaUID> };
on?: { [x: string]: boolean | NestedParams<TMaybeTargets> };
};
type PopulateClause<
@ -108,9 +108,9 @@ export type ObjectNotation<TSchemaUID extends Common.UID.Schema> = [
}
>,
// Loose fallback when registries are not extended
| { [TKey in string]?: boolean | NestedParams<Common.UID.Schema> }
| { [x: string]: boolean | NestedParams<Common.UID.Schema> }
| {
[TKey in string]?:
[x: string]:
| boolean
| Fragment<Common.UID.Schema>
// TODO: V5: Remove root-level nested params for morph data structures and only allow fragments

View File

@ -61,7 +61,9 @@ export type StringNotation<TSchemaUID extends Common.UID.Schema> =
* type E = [42]; // ❌
* type F = 'title'; // ❌
*/
export type ArrayNotation<TSchemaUID extends Common.UID.Schema> = Any<TSchemaUID>[];
export type ArrayNotation<TSchemaUID extends Common.UID.Schema> =
| StringNotation<TSchemaUID>[]
| ObjectNotation<TSchemaUID>[];
/**
* Object notation for a sort
@ -74,15 +76,21 @@ export type ArrayNotation<TSchemaUID extends Common.UID.Schema> = Any<TSchemaUID
* type E = ['title']; // ❌
* type F = 'title'; // ❌
*/
export type ObjectNotation<TSchemaUID extends Common.UID.Schema> = {
[TKey in ObjectNotationKeys<TSchemaUID>]?: TKey extends SingleAttribute<TSchemaUID>
? // First level sort (scalar attributes, id, ...)
OrderKind.Any
: TKey extends Attribute.GetKeysWithTarget<TSchemaUID>
? // Deep sort (relations with a target, components, media, ...)
ObjectNotation<Attribute.GetTarget<TSchemaUID, TKey>>
: never;
};
export type ObjectNotation<TSchemaUID extends Common.UID.Schema> = Utils.Expression.If<
Common.AreSchemaRegistriesExtended,
{
[TKey in ObjectNotationKeys<TSchemaUID>]?: TKey extends SingleAttribute<TSchemaUID>
? // First level sort (scalar attributes, id, ...)
OrderKind.Any
: TKey extends Attribute.GetKeysWithTarget<TSchemaUID>
? // Deep sort (relations with a target, components, media, ...)
ObjectNotation<Attribute.GetTarget<TSchemaUID, TKey>>
: never;
},
{
[x: string]: OrderKind.Any | ObjectNotation<TSchemaUID>;
}
>;
/**
* Represents the keys of an object notation for a sort

View File

@ -61,7 +61,7 @@ export interface UniqueOption {
}
export interface DefaultOption<T> {
default?: T;
default?: T | (() => T);
}
export interface ConfigurableOption {

View File

@ -56,7 +56,6 @@
},
"devDependencies": {
"@strapi/pack-up": "4.17.1",
"@strapi/types": "4.17.1",
"@types/koa": "2.13.4",
"@types/node": "18.18.4",
"eslint-config-custom": "4.17.1",

View File

@ -1,5 +1,4 @@
import { traverseQueryPopulate } from '../traverse';
import { setGlobalStrapi, getStrapiFactory } from './test-utils';
describe('traverseQueryPopulate', () => {
test('should return an empty object incase no populatable field exists', async () => {
@ -18,7 +17,7 @@ describe('traverseQueryPopulate', () => {
});
test('should return all populatable fields', async () => {
const strapi = getStrapiFactory({
const strapi = {
getModel: jest.fn((uid) => {
return {
uid,
@ -39,9 +38,9 @@ describe('traverseQueryPopulate', () => {
})),
},
},
})();
} as any;
setGlobalStrapi(strapi);
global.strapi = strapi;
const query = await traverseQueryPopulate(jest.fn(), {
schema: {
@ -68,7 +67,7 @@ describe('traverseQueryPopulate', () => {
});
test('should return only selected populatable field', async () => {
const strapi = getStrapiFactory({
const strapi = {
getModel: jest.fn((uid) => {
return {
uid,
@ -88,9 +87,9 @@ describe('traverseQueryPopulate', () => {
})),
},
},
})();
} as any;
setGlobalStrapi(strapi);
global.strapi = strapi;
const query = await traverseQueryPopulate(jest.fn(), {
schema: {
@ -117,7 +116,7 @@ describe('traverseQueryPopulate', () => {
});
test('should populate dynamiczone', async () => {
const strapi = getStrapiFactory({
const strapi = {
getModel: jest.fn((uid) => {
return {
uid,
@ -137,9 +136,9 @@ describe('traverseQueryPopulate', () => {
})),
},
},
})();
} as any;
setGlobalStrapi(strapi);
global.strapi = strapi;
const query = await traverseQueryPopulate(jest.fn(), {
schema: {
@ -174,7 +173,7 @@ describe('traverseQueryPopulate', () => {
});
test('should deep populate dynamiczone components', async () => {
const strapi = getStrapiFactory({
const strapi = {
getModel: jest.fn((uid) => {
if (uid === 'blog.test-como') {
return {
@ -224,9 +223,9 @@ describe('traverseQueryPopulate', () => {
})),
},
},
})();
} as any;
setGlobalStrapi(strapi);
global.strapi = strapi;
const query = await traverseQueryPopulate(jest.fn(), {
schema: {

View File

@ -1,24 +0,0 @@
import type { LoadedStrapi } from '@strapi/types';
/**
* Update the global store with the given strapi value
*/
export const setGlobalStrapi = (strapi: LoadedStrapi): void => {
(global as unknown as Global).strapi = strapi;
};
/**
* Create a "Strapi" like object factory based on the
* given params and cast it to the correct type
*/
export const getStrapiFactory =
<
T extends {
[key in keyof Partial<LoadedStrapi>]: unknown;
}
>(
properties?: T
) =>
(additionalProperties?: Partial<T>) => {
return { ...properties, ...additionalProperties } as LoadedStrapi;
};

View File

@ -43,15 +43,22 @@ type FieldsParams = string | string[];
type FiltersParams = unknown;
export interface PopulateAttributesParams {
[key: string]: PopulateObjectParams;
[key: string]: boolean | PopulateObjectParams;
}
export interface PopulateObjectParams {
sort?: SortParams;
fields?: FieldsParams;
filters?: FiltersParams;
populate?: PopulateParams;
populate?: string | string[] | PopulateAttributesParams;
publicationState?: 'live' | 'preview';
on: PopulateAttributesParams;
on?: PopulateAttributesParams;
count?: boolean;
ordering?: unknown;
_q?: string;
limit?: number | string;
start?: number | string;
page?: number | string;
pageSize?: number | string;
}
type PopulateParams = string | string[] | PopulateAttributesParams;
@ -314,6 +321,12 @@ const convertPopulateQueryParams = (
throw new InvalidPopulateError();
};
const hasFragmentPopulateDefined = (
populate: PopulateObjectParams
): populate is PopulateObjectParams & Required<Pick<PopulateObjectParams, 'on'>> => {
return typeof populate === 'object' && 'on' in populate && !isNil(populate.on);
};
const convertPopulateObject = (populate: PopulateAttributesParams, schema?: Model) => {
if (!schema) {
return {};
@ -322,6 +335,10 @@ const convertPopulateObject = (populate: PopulateAttributesParams, schema?: Mode
const { attributes } = schema;
return Object.entries(populate).reduce((acc, [key, subPopulate]) => {
if (_.isBoolean(subPopulate)) {
return { ...acc, [key]: subPopulate };
}
const attribute = attributes[key];
if (!attribute) {
@ -332,10 +349,10 @@ const convertPopulateObject = (populate: PopulateAttributesParams, schema?: Mode
const isAllowedAttributeForFragmentPopulate =
isDynamicZoneAttribute(attribute) || isMorphToRelationalAttribute(attribute);
const hasFragmentPopulateDefined =
typeof subPopulate === 'object' && 'on' in subPopulate && !isNil(subPopulate.on);
// const hasFragmentPopulateDefined =
// typeof subPopulate === 'object' && 'on' in subPopulate && !isNil(subPopulate.on);
if (isAllowedAttributeForFragmentPopulate && hasFragmentPopulateDefined) {
if (isAllowedAttributeForFragmentPopulate && hasFragmentPopulateDefined(subPopulate)) {
return {
...acc,
[key]: {
@ -408,7 +425,7 @@ const convertPopulateObject = (populate: PopulateAttributesParams, schema?: Mode
}, {});
};
const convertNestedPopulate = (subPopulate: PopulateObjectParams, schema?: Model) => {
const convertNestedPopulate = (subPopulate: boolean | PopulateObjectParams, schema?: Model) => {
if (_.isString(subPopulate)) {
return parseType({ type: 'boolean', value: subPopulate, forceCast: true });
}
@ -422,7 +439,7 @@ const convertNestedPopulate = (subPopulate: PopulateObjectParams, schema?: Model
}
const { sort, filters, fields, populate, count, ordering, page, pageSize, start, limit } =
subPopulate;
subPopulate as PopulateObjectParams;
const query: Query = {};

View File

@ -154,6 +154,8 @@ const build = async (opts: BuildOptions = {}) => {
},
error(err) {
handler.fail(ctx, task, err);
// exit as soon as one task fails
process.exit(1);
},
});
}

View File

@ -63,14 +63,12 @@ const dtsBuildTask: TaskHandler<DtsBuildTask> = {
printDiagnostic(diagnostic, { logger: ctx.logger, cwd: ctx.cwd });
}
if (emitResult.emitSkipped) {
const errors = allDiagnostics.filter(
(diag) => diag.category === ts.DiagnosticCategory.Error
);
const errors = allDiagnostics.filter(
(diag) => diag.category === ts.DiagnosticCategory.Error
);
if (errors.length) {
throw new Error('Failed to compile TypeScript definitions');
}
if (errors.length) {
throw new Error('Failed to compile TypeScript definitions');
}
})
)
@ -99,8 +97,6 @@ const dtsBuildTask: TaskHandler<DtsBuildTask> = {
if (isError(err)) {
ctx.logger.error(err.message);
}
process.exit(1);
},
};

View File

@ -9605,6 +9605,7 @@ __metadata:
"@strapi/icons": "npm:1.14.1"
"@strapi/pack-up": "npm:4.17.1"
"@strapi/types": "npm:4.17.1"
"@strapi/utils": "npm:4.17.1"
"@testing-library/react": "npm:14.0.0"
"@testing-library/user-event": "npm:14.4.3"
axios: "npm:1.6.0"
@ -10243,10 +10244,13 @@ __metadata:
"@strapi/types": "npm:4.17.1"
"@strapi/typescript-utils": "npm:4.17.1"
"@strapi/utils": "npm:4.17.1"
"@types/find-root": "npm:1.1.4"
"@types/jest": "npm:29.5.2"
"@types/lodash": "npm:^4.14.191"
"@types/node": "npm:18.18.4"
"@types/nodemon": "npm:1.19.6"
"@types/webpack-bundle-analyzer": "npm:4.6.3"
"@types/webpack-hot-middleware": "npm:2.25.9"
"@vitejs/plugin-react-swc": "npm:3.5.0"
boxen: "npm:5.1.2"
browserslist: "npm:^4.22.2"
@ -10422,7 +10426,6 @@ __metadata:
dependencies:
"@sindresorhus/slugify": "npm:1.1.0"
"@strapi/pack-up": "npm:4.17.1"
"@strapi/types": "npm:4.17.1"
"@types/koa": "npm:2.13.4"
"@types/node": "npm:18.18.4"
date-fns: "npm:2.30.0"
@ -11207,6 +11210,13 @@ __metadata:
languageName: node
linkType: hard
"@types/find-root@npm:1.1.4":
version: 1.1.4
resolution: "@types/find-root@npm:1.1.4"
checksum: e438c62b3ef3b706d058764797ec5227479ed4be98e9fec5456fb3b9a2096ccd3e35a84c94e2018278e22c651f12ae6ff4d6d7006b09fa5132da3612e2118076
languageName: node
linkType: hard
"@types/fined@npm:*":
version: 1.1.3
resolution: "@types/fined@npm:1.1.3"
@ -12197,6 +12207,28 @@ __metadata:
languageName: node
linkType: hard
"@types/webpack-bundle-analyzer@npm:4.6.3":
version: 4.6.3
resolution: "@types/webpack-bundle-analyzer@npm:4.6.3"
dependencies:
"@types/node": "npm:*"
tapable: "npm:^2.2.0"
webpack: "npm:^5"
checksum: 646b78aa5e06094b9558d49826fcecff6d7c67ab5e02120b9567a4cec904aaebb820ffac5f57963aada98a0603ae56c8aebfd9b54e8bb0597540e6687da063ac
languageName: node
linkType: hard
"@types/webpack-hot-middleware@npm:2.25.9":
version: 2.25.9
resolution: "@types/webpack-hot-middleware@npm:2.25.9"
dependencies:
"@types/connect": "npm:*"
tapable: "npm:^2.2.0"
webpack: "npm:^5"
checksum: bfa30ed24fcad7f4e4bce956ee7f2ddcc332428be38c2fa3613e8c53bd077d19806dc801de9c36cb77b966b38c9c9e27ef0367da25b684237f793955e406b657
languageName: node
linkType: hard
"@types/ws@npm:^8.5.4":
version: 8.5.4
resolution: "@types/ws@npm:8.5.4"
@ -33331,7 +33363,7 @@ __metadata:
languageName: node
linkType: hard
"webpack@npm:^5.89.0":
"webpack@npm:^5, webpack@npm:^5.89.0":
version: 5.89.0
resolution: "webpack@npm:5.89.0"
dependencies: