xenopomp-essentials 0.5.0-canary.4 → 0.5.0-canary.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ "use strict";const r$1=require("node:path"),zod=require("zod"),e=require("dotenv");var _documentCurrentScript=typeof document<"u"?document.currentScript:null;function _interopDefaultCompat(t){return t&&typeof t=="object"&&"default"in t?t.default:t}const r__default=_interopDefaultCompat(r$1),e__default=_interopDefaultCompat(e),o=zod.z.object({dirname:zod.z.string().optional()}),importMeta=o.parse({url:typeof document>"u"?require("url").pathToFileURL(__filename).href:_documentCurrentScript&&_documentCurrentScript.tagName.toUpperCase()==="SCRIPT"&&_documentCurrentScript.src||new URL("cli-tools/index.cjs",document.baseURI).href});e__default.config({quiet:!0});const r=zod.z.object({DISABLE_MINIFY:zod.z.string().optional().transform(t=>t==="true")});r.parse(process.env);class PathBuilder{paths=[];pushPaths(...s){return this.paths.push(...s),this}cd(...s){return this.pushPaths(...s)}file(s){return this.pushPaths(`./${s}`)}appSource(){return importMeta.dirname?this.pushPaths(r__default.join(r__default.dirname(importMeta.dirname),"../")):this.cwd()}cwd(){return this.pushPaths(process.cwd())}clear(){return this.paths=[],this}build(){return r__default.join(...this.paths)}}exports.PathBuilder=PathBuilder;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Fluent interface for creating paths of application.
3
+ *
4
+ * @since 0.0.1
5
+ *
6
+ * @example
7
+ * import { PathBuilder } from 'xenopomp-essentials/cli-tools';
8
+ *
9
+ * const builder = new PathBuilder();
10
+ *
11
+ * const res = builder
12
+ * .appSource()
13
+ * .cd('./dist/src')
14
+ * .file('.prettierrc')
15
+ * .build();
16
+ *
17
+ * console.log(res); //? C:/Projects/example/node_modules/xenopomp-essentials/dist/src/.prettierrc
18
+ */
19
+ declare class PathBuilder {
20
+ private paths;
21
+ private pushPaths;
22
+ /**
23
+ * Pushes any custom paths to builder.
24
+ * @since 0.0.1
25
+ * @param paths
26
+ */
27
+ cd(...paths: string[]): PathBuilder;
28
+ /**
29
+ * Pushes filename to paths.
30
+ * @since 0.0.1
31
+ * @param fileName
32
+ */
33
+ file<T extends `${string}.${string}`>(fileName: T): PathBuilder;
34
+ /**
35
+ * Pushes path to compiled app directory.
36
+ * Is useful for cli tools that have to access files inside their bundles.
37
+ *
38
+ * It probably may take no effect (if import.meta is not available in a scope).
39
+ * If it is, will push cwd.
40
+ *
41
+ * @since 0.0.1
42
+ */
43
+ appSource(): PathBuilder;
44
+ /**
45
+ * Gets path of directory where script is running. In cli tools it access
46
+ * path, where cli tool was started.
47
+ * @since 0.0.1
48
+ */
49
+ cwd(): PathBuilder;
50
+ /**
51
+ * @since 0.0.1
52
+ */
53
+ clear(): PathBuilder;
54
+ /**
55
+ * @since 0.0.1
56
+ */
57
+ build(): string;
58
+ }
59
+
60
+ export { PathBuilder };
@@ -0,0 +1 @@
1
+ "use strict";const eslintConfig=require("@antfu/eslint-config"),deepmergeTs=require("deepmerge-ts"),t=require("globals"),e=require("eslint-plugin-prettier/recommended"),compat=require("@eslint/compat"),o=require("eslint-plugin-deprecation");function _interopDefaultCompat(n){return n&&typeof n=="object"&&"default"in n?n.default:n}const t__default=_interopDefaultCompat(t),e__default=_interopDefaultCompat(e),o__default=_interopDefaultCompat(o),AUTHOR_NAME="XenoPOMP",AUTHOR_NAME_LOWER=AUTHOR_NAME.toLowerCase();async function react(){return[{files:[eslintConfig.GLOB_JSX,eslintConfig.GLOB_TSX],name:`${AUTHOR_NAME_LOWER}:react`,rules:{"react-refresh/only-export-components":"off","react/no-clone-element":"off","react/no-missing-component-display-name":"off"}}]}async function next(){const n=await eslintConfig.interopDefault(import("@next/eslint-plugin-next"));return[{name:`${AUTHOR_NAME_LOWER}:next`,plugins:{"@next/next":n},rules:{"@next/next/google-font-display":"warn","@next/next/google-font-preconnect":"warn","@next/next/next-script-for-ga":"warn","@next/next/no-async-client-component":"warn","@next/next/no-before-interactive-script-outside-document":"warn","@next/next/no-css-tags":"warn","@next/next/no-head-element":"warn","@next/next/no-html-link-for-pages":"warn","@next/next/no-img-element":"warn","@next/next/no-page-custom-font":"warn","@next/next/no-styled-jsx-in-document":"warn","@next/next/no-sync-scripts":"warn","@next/next/no-title-in-document-head":"warn","@next/next/no-typos":"warn","@next/next/no-unwanted-polyfillio":"warn","@next/next/inline-script-id":"error","@next/next/no-assign-module-variable":"error","@next/next/no-document-import-in-page":"error","@next/next/no-duplicate-head":"error","@next/next/no-head-import-in-document":"error","@next/next/no-script-component-in-head":"error"}}]}async function markdown(){return[{name:`${AUTHOR_NAME_LOWER}:markdown`,files:[eslintConfig.GLOB_MARKDOWN_CODE],rules:{"import/no-unresolved":"off","unused-imports/no-unused-imports":"off","unused-imports/no-unused-vars":"off","no-alert":"off","no-console":"off","no-restricted-imports":"off","no-undef":"off","no-unused-expressions":"off","no-unused-vars":"off","antfu/no-cjs-exports":"off","antfu/no-ts-export-equal":"off","ts/no-redeclare":"off","ts/no-unused-vars":"off","ts/no-var-requires":"off","ts/consistent-type-imports":"off"}}]}async function all(){return[{name:`${AUTHOR_NAME_LOWER}:all`,rules:{"antfu/no-cjs-exports":"off","import/prefer-default-export":"off","import/extensions":"off","no-restricted-globals":"off","node/prefer-global/process":"off","jsonc/comma-dangle":"off","style/jsx-quotes":["error","prefer-double"],"style/jsx-one-expression-per-line":["error",{allow:"single-line"}],"style/arrow-parens":["error","as-needed"],"unused-imports/no-unused-imports":"error","unused-imports/no-unused-vars":["warn",{vars:"all",varsIgnorePattern:"^_",args:"after-used",argsIgnorePattern:"^_"}],"@stylistic/indent":["error",2,{ArrayExpression:1,CallExpression:{arguments:1},flatTernaryExpressions:!1,FunctionDeclaration:{body:1,parameters:1},FunctionExpression:{body:1,parameters:1},ignoreComments:!1,ignoredNodes:["TemplateLiteral *","TSIntersectionType","TSTypeParameterInstantiation","FunctionExpression > .params[decorators.length > 0]","FunctionExpression > .params > :matches(Decorator, :not(:first-child))","ClassBody.body > PropertyDefinition[decorators.length > 0] > .key"],ImportDeclaration:1,MemberExpression:1,ObjectExpression:1,offsetTernaryExpressions:!0,outerIIFEBody:1,SwitchCase:1,VariableDeclarator:1}]}}]}async function old(){return[{name:`${AUTHOR_NAME_LOWER}:old_config`,languageOptions:{globals:{...t__default.browser,...t__default.jquery,...t__default.node,document:"readonly",navigator:"readonly",window:"readonly"}},ignores:["**/.next/*","**/node_modules/*","**/.github/*","cypress","**/__tests__/e2e/*","*.json","**/*.d.ts",".eslintrc.js","eslint.config.js",".prettierrc",".stylelintrc.js","tsconfig.json","package.json","*.md","*.config.ts","*.config.js","*.md"]},{name:`${AUTHOR_NAME_LOWER}:rules_breakup_1`,rules:{"@next/next/no-duplicate-head":"off"}},{name:`${AUTHOR_NAME_LOWER}:rules_breakup_2`,rules:{"style/operator-linebreak":"off","test/consistent-test-it":"off","test/prefer-lowercase-title":"off","style/jsx-quotes":"off","style/multiline-ternary":"off","style/indent":"off"},ignores:["cypress"]},{name:`${AUTHOR_NAME_LOWER}:prettier`,rules:{"antfu/if-newline":"off"}}]}async function prettier(){return[e__default]}async function deprecation(n,s){return[{name:"Deprecation plugin",languageOptions:{parserOptions:{project:s?.tsconfigPath??"./tsconfig.json",projectSource:!0}},plugins:{deprecation:compat.fixupPluginRules(o__default)},rules:{"deprecation/deprecation":n},files:["**/*.ts","**/*.tsx"]}]}function m(n,...s){const r=[];return n={...n,react:deepmergeTs.deepmerge(!0,n?.react),vue:deepmergeTs.deepmerge(!1,n?.vue),jsonc:deepmergeTs.deepmerge(!1,n?.jsonc),yaml:deepmergeTs.deepmerge(!1,n?.yaml),stylistic:deepmergeTs.deepmerge({semi:!0,quotes:"single"},n?.stylistic),typescript:deepmergeTs.deepmerge({overrides:{"ts/consistent-type-definitions":["error","interface"],"ts/interface-name-prefix":"off","ts/explicit-function-return-type":"off","ts/explicit-module-boundary-types":"off","ts/no-explicit-any":"off"}},n?.typescript),rules:deepmergeTs.deepmerge({"import/order":"off","react/react-in-jsx-scope":"off","react/prop-types":"off","antfu/top-level-function":"off","perfectionist/sort-imports":"off","perfectionist/sort-named-imports":"off","perfectionist/sort-named-exports":"off","antfu/consistent-chaining":"off","perfectionist/sort-exports":"off","style/no-multiple-empty-lines":"off"},n?.rules),deprecation:n?.deprecation,tsconfigPath:n?.tsconfigPath},(n.react??!0)&&r.push(react()),(n.all??!0)&&r.push(all()),(n.next??!1)&&r.push(next()),(n.markdown??!0)&&r.push(markdown()),n?.deprecation&&r.push(deprecation(n?.deprecation??"warn",{tsconfigPath:n?.tsconfigPath})),r.push(old()),r.push(prettier()),eslintConfig.antfu(n,...r,...s)}module.exports=m;
@@ -0,0 +1,40 @@
1
+ import { antfu, OptionsConfig, TypedFlatConfigItem } from '@antfu/eslint-config';
2
+ import { Linter } from 'eslint';
3
+
4
+ interface CustomConfig {
5
+ all?: boolean;
6
+ next?: boolean;
7
+ }
8
+
9
+ type UserConfigItem = Parameters<typeof antfu>[1];
10
+ type Configs = ReturnType<typeof antfu>;
11
+
12
+ interface CustomOptions {
13
+ deprecation?: Linter.RuleEntry;
14
+ tsconfigPath?: string;
15
+ }
16
+ type Options = OptionsConfig & CustomConfig & TypedFlatConfigItem & CustomOptions;
17
+ /**
18
+ * XenoPOMP`s default ESLint config. Uses @antfu/eslint-config under the hood.
19
+ *
20
+ * @param options
21
+ * @param userConfigs
22
+ *
23
+ * @example
24
+ * export default xenopomp(
25
+ * // Setup antfu config
26
+ * {
27
+ * react: true,
28
+ * next: false,
29
+ * },
30
+ * // User configs
31
+ * {
32
+ * name: 'My custom config',
33
+ * rules: { ... }
34
+ * },
35
+ * );
36
+ */
37
+ declare function xenopomp(options?: Options, ...userConfigs: UserConfigItem[]): Configs;
38
+
39
+ export = xenopomp;
40
+ export type { Configs, CustomConfig, Options, UserConfigItem };
package/eslint/index.d.ts CHANGED
@@ -36,5 +36,5 @@ type Options = OptionsConfig & CustomConfig & TypedFlatConfigItem & CustomOption
36
36
  */
37
37
  declare function xenopomp(options?: Options, ...userConfigs: UserConfigItem[]): Configs;
38
38
 
39
- export { xenopomp as default };
39
+ export = xenopomp;
40
40
  export type { Configs, CustomConfig, Options, UserConfigItem };
package/index.cjs ADDED
@@ -0,0 +1 @@
1
+ "use strict";const transliteration=require("transliteration");function pipe(t){function e(r){return t(r)}return e.pipe=r=>pipe(i=>r(t(i))),e}function capitalize(t){return t.charAt(0).toUpperCase()+t.slice(1)}function uncapitalize(t){return t.charAt(0).toLowerCase()+t.slice(1)}const parseVersion=t=>{const e=t.match(/(\d\.){2}\d/gi)?.at(0)||null,r=t.match(/(?<=-)\w+(?=\.\d)/gi)?.at(0),i=t.match(/(?<=((\d\.){2}\d-\w+\.))\d+/gi)?.at(0);return{version:e,preid:r,prerelease:i}},transliterate=transliteration.transliterate,minmax=(t,[e,r])=>e&&t<=e?e:r&&t>=r?r:t;function jsxDotNotation(t,e){return Object.assign(t,e)}exports.capitalize=capitalize,exports.jsxDotNotation=jsxDotNotation,exports.minmax=minmax,exports.parseVersion=parseVersion,exports.pipe=pipe,exports.transliterate=transliterate,exports.uncapitalize=uncapitalize;
package/index.d.cts ADDED
@@ -0,0 +1,610 @@
1
+ export { O as Optional, P as PromiseOr, U as Undefinable } from './shared/xenopomp-essentials.CO5lcTlK.cjs';
2
+ import { ElementType, ComponentProps, FC, ReactNode, Dispatch, SetStateAction } from 'react';
3
+ import { Jsonifiable } from 'type-fest';
4
+ import * as transliteration from 'transliteration';
5
+
6
+ /**
7
+ * Matches any object but not arrays, class instances etc.
8
+ * @since 0.0.1
9
+ */
10
+ type AnyObject = Record<PropertyKey, unknown>;
11
+
12
+ /**
13
+ * This type stands for empty object type.
14
+ *
15
+ * @since 0.0.1
16
+ * @example
17
+ * const message: EmptyObject = { type: string; }; // Will cause error.
18
+ */
19
+ type EmptyObject = Record<PropertyKey, never>;
20
+
21
+ /**
22
+ * Simple alias for creating function types. You can use it for type guards
23
+ * as representation of any function, also you can create cleaner type declarations
24
+ * for functions.
25
+ *
26
+ * @since 0.0.1
27
+ * @example
28
+ * type MyFn = Fn<[a: string, b: string], string>;
29
+ */
30
+ type Fn<Args extends any[] = any[], Res = unknown> = (...args: Args) => Res;
31
+
32
+ type ParamObj<P extends string> = Record<P, string>;
33
+ /**
34
+ * Represents Next.js routing params.
35
+ * @since 0.0.1
36
+ */
37
+ interface NextParams<P extends string> {
38
+ params: ParamObj<P>;
39
+ }
40
+ /**
41
+ * Represents Next.js routing search params.
42
+ * @since 0.0.1
43
+ */
44
+ interface NextSearchParams<P extends string> {
45
+ searchParams: ParamObj<P>;
46
+ }
47
+ /**
48
+ * This type describes Next error page params.
49
+ * @since 0.0.1
50
+ */
51
+ interface NextErrorParams<E extends Error = any> {
52
+ error: E;
53
+ reset: () => void;
54
+ }
55
+
56
+ /**
57
+ * This type wraps ComponentProps<A> to your FC.
58
+ *
59
+ * - __A__ - element type
60
+ * - __P__ - wrapping props
61
+ * - __Ex__ - excluded component props (for example, if you do not want to include 'children' prop in component)
62
+ *
63
+ * @since 0.0.1
64
+ *
65
+ * @example
66
+ * type VariableComponentPropsWithChildren = ComponentProps<VariableFC<'div'>>;
67
+ * type VariableComponentPropsWithoutChildren = ComponentProps<VariableFC<'div', {}, 'children'>>;
68
+ *
69
+ * @example
70
+ * // With children
71
+ * const VariableComponent: VariableFC<'section', { variant?: 'default' }> = ({ variant, ...props }) => {
72
+ * return <section {...props}></section>
73
+ * }
74
+ *
75
+ * @example
76
+ * // Without children
77
+ * const VariableComponent: VariableFC<'section', { variant?: 'default' }, 'children'> = ({ variant, ...props }) => {
78
+ * return (
79
+ * <section {...props}>
80
+ * <div>Insert your own children</div>
81
+ * </section>
82
+ * );
83
+ * }
84
+ */
85
+ type VariableFC<A extends ElementType, P = unknown, Ex extends keyof ComponentProps<A> | undefined = undefined> = FC<P & WeakOmit<ComponentProps<A>, Ex>>;
86
+ /**
87
+ * Works similar to {@link VariableFC}, but return type is Promise<ReactNode>;
88
+ * @since 0.0.1
89
+ */
90
+ type AsyncVariableFC<A extends ElementType, P = unknown, Ex extends keyof ComponentProps<A> | undefined = undefined> = AsyncFC<P & WeakOmit<ComponentProps<A>, Ex>>;
91
+
92
+ /**
93
+ * Same as **FC** (or FunctionalComponent), but it returns Promise.
94
+ *
95
+ * @since 0.0.1
96
+ * @example
97
+ * const Header: AsyncFC<HeaderProps> = async ({}) => {
98
+ * // Do async stuff here...
99
+ *
100
+ * return <div></div>;
101
+ * };
102
+ */
103
+ type AsyncFC<T = unknown> = ReplaceReturnType<FC<T>, Promise<ReactNode>>;
104
+
105
+ /**
106
+ * Type of set state function from useState.
107
+ *
108
+ * @since 0.0.1
109
+ * @example
110
+ * // typeof setState => SetState<string>
111
+ * const [state, setState] = useState<string>('example');
112
+ */
113
+ type SetState<TType> = Dispatch<SetStateAction<TType>>;
114
+
115
+ /**
116
+ * This type allows you to make children that can be function.
117
+ *
118
+ * @since 0.0.1
119
+ * @example
120
+ * type Child = FunctionalChildren<[options: string]>;
121
+ * // ReactNode | ((options: string) => ReactNode)
122
+ */
123
+ type FunctionalChildren<Args extends any[]> = ReactNode | ((...args: Args) => ReactNode);
124
+
125
+ /**
126
+ * @deprecated use {@link FCProps} instead.
127
+ */
128
+ type FcProps<Comp> = FCProps<Comp>;
129
+ /**
130
+ * Extracts type of props from FC type.
131
+ *
132
+ * @since 0.0.1
133
+ * @example
134
+ * type Props = FcProps<FC<{ align?: boolean }>>;
135
+ * // ^? { align?: boolean }
136
+ *
137
+ * type VariableProps = FcProps<VariableFC<'button', { notAlign?: boolean }>>;
138
+ * // ^? {notAlign?: boolean} & Omit<ClassAttributes<HTMLButtonElement> & React.ButtonHTMLAttributes<HTMLButtonElement>, undefined>
139
+ */
140
+ type FCProps<Comp> = Comp extends FC<infer Props> ? Props : never;
141
+
142
+ /**
143
+ * Matches any proper React component.
144
+ */
145
+ type AnyFC = FC<unknown>;
146
+
147
+ /**
148
+ * Type that represents data-{attr} keys.
149
+ */
150
+ type Attribute = `data-${string}`;
151
+ /**
152
+ * Matches any proper data-{attr} keys.
153
+ */
154
+ type DataAttributeShape = Record<Attribute, any>;
155
+ /**
156
+ * Represents React`s data-{attr} properties for components.
157
+ *
158
+ * @example
159
+ * type AdditionalAttributes = DataAttributes<{ 'data-disabled': boolean }>;
160
+ *
161
+ * const attrs: AdditionalAttributes = {
162
+ * 'data-disabled': false, // this attr is required
163
+ * };
164
+ */
165
+ type DataAttributes<Shape extends DataAttributeShape> = Shape;
166
+
167
+ type VariableProps<A extends ElementType, P = unknown, Ex extends keyof ComponentProps<A> | undefined = undefined> = FCProps<VariableFC<A, P, Ex>>;
168
+
169
+ /**
170
+ * Make any async func synchronous.
171
+ *
172
+ * Return never, if __Func__ is not a function, actually.
173
+ *
174
+ * @since 0.0.1
175
+ * @example
176
+ * type NormalFunc = Synchronous<(one: string, two: number) => void>; // (one: string, two: number) => void
177
+ * type AsyncFunc = Synchronous<(one: string, two: number) => Promise<void>>; // (one: string, two: number) => void
178
+ * type NotAFunc = Synchronous<'sus'>; // never
179
+ */
180
+ type Synchronous<Func> = Func extends Fn ? Func extends (...args: any[]) => Promise<infer Result> ? ReplaceReturnType<Func, Result> : Func : never;
181
+
182
+ /**
183
+ * Removes readonly from type.
184
+ * @since 0.0.1
185
+ */
186
+ type Writeable<T> = {
187
+ -readonly [P in keyof T]: T[P];
188
+ };
189
+ /**
190
+ * @deprecated Use {@link WriteableDeep} instead.
191
+ */
192
+ type DeepWriteable<T> = WriteableDeep<T>;
193
+ /**
194
+ * Removes readonly from type deeply.
195
+ * @since 0.0.1
196
+ */
197
+ type WriteableDeep<T> = {
198
+ -readonly [P in keyof T]: DeepWriteable<T[P]>;
199
+ };
200
+
201
+ /**
202
+ * Checks if type **T** matches type **M**.
203
+ *
204
+ * @since 0.0.1
205
+ *
206
+ * @example Not matching
207
+ * type ViewStyles = {
208
+ * paddingHorizontal: number
209
+ * }
210
+ *
211
+ * type Styles = MatchType<{}, ViewStyles>; // TSERROR
212
+ *
213
+ * @example Matching
214
+ * type ViewStyles = {
215
+ * paddingHorizontal: number;
216
+ * };
217
+ *
218
+ * // No error
219
+ * type Styles = MatchType<
220
+ * {
221
+ * paddingHorizontal: number;
222
+ * },
223
+ * ViewStyles
224
+ * >;
225
+ */
226
+ type MatchType<T extends M, M> = T extends M ? T : never;
227
+
228
+ /**
229
+ * This type gets type of array`s item.
230
+ *
231
+ * @since 0.0.1
232
+ * @example
233
+ * type Super = ArrayType<string[]>; // string
234
+ *
235
+ * type Good = ArrayType<(string|number)[]>; // string | number
236
+ */
237
+ type ArrayType<T> = T extends Array<infer A> ? A : never;
238
+ type ArrayItemType<T> = ArrayType<T>;
239
+
240
+ /**
241
+ * Get return type of function that returns Promise ((...args: any) => Promise<any>).
242
+ *
243
+ * @since 0.0.1
244
+ * @example
245
+ * const doSomething = async (): Promise<string|number> => {
246
+ * return 'result';
247
+ * }
248
+ *
249
+ * type Something = PromiseReturnType<typeof doSomething>;
250
+ * // string|number
251
+ */
252
+ type AsyncReturnType<F extends (...args: any[]) => Promise<any>> = Awaited<ReturnType<F>>;
253
+
254
+ /**
255
+ * @deprecated Use {@link InjectDeep} instead.
256
+ */
257
+ type DeepInject<T, I extends AnyObject> = InjectDeep<T, I>;
258
+ /**
259
+ * Recursively add some type inside all keys of target type.
260
+ *
261
+ * @since 0.0.1
262
+ * @example
263
+ * type Sups = DeepInject<{ supa: { sups: number } }, { _ignore: boolean }>;
264
+ * const asp: Sups = { supa: { sups: 1, _ignore: false }, _ignore: false };
265
+ */
266
+ type InjectDeep<T, I extends AnyObject> = T extends object ? {
267
+ [K in keyof T]: T[K] extends object ? T[K] & I & DeepInject<T[K], I> : T[K];
268
+ } & I : T;
269
+
270
+ /**
271
+ * Removes undefined from union type.
272
+ * @since 0.0.1
273
+ * @deprecated Use {@link NonNullable} instead.
274
+ */
275
+ type Defined<T> = Exclude<T, undefined>;
276
+
277
+ /**
278
+ * Return never if type does not match JSON schema.
279
+ *
280
+ * @since 0.0.1
281
+ * @example
282
+ * type Schema = Jsonish<{ sus: string; am: string; }>; // OK
283
+ * type AlterSchema = Jsonish<{ sus: string; am: () => {}; }>; // Causes error
284
+ */
285
+ type Jsonish<T extends Jsonifiable> = T extends Jsonifiable ? T : never;
286
+
287
+ /**
288
+ * Modifies K (Key) in T (Type), replace it with R (Replacement).
289
+ *
290
+ * @since 0.0.1
291
+ * @example
292
+ * type ExcludedStore = Modify<IStore, 'appSettings', { appName: 'Simple name' }>; // Key in IStore has been replaced with new type.
293
+ */
294
+ type Modify<T, K extends keyof T, R> = Omit<T, K> & {
295
+ [Key in K]: R;
296
+ };
297
+
298
+ /**
299
+ * Make union type of T and null.
300
+ * @since 0.0.1
301
+ */
302
+ type Nullable<T> = T | null;
303
+
304
+ /**
305
+ * Get typeof key of Record.
306
+ *
307
+ * @since 0.0.1
308
+ * @example
309
+ * type Key = RecordKey<Record<string, number>>; // string
310
+ */
311
+ type RecordKey<R extends Record<any, any>> = R extends Record<infer K, any> ? K : never;
312
+
313
+ /**
314
+ * Get typeof value of Record.
315
+ *
316
+ * @since 0.0.1
317
+ * @example
318
+ * type Value = RecordValue<Record<string, number>>; // number
319
+ */
320
+ type RecordValue<R extends Record<any, any>> = R extends Record<any, infer V> ? V : never;
321
+
322
+ /**
323
+ * This type replace return type of function with other type.
324
+ *
325
+ * @since 0.0.1
326
+ * @example
327
+ * type StringFC<T = {}> = ReplaceReturnType<FC<T>, string>;
328
+ */
329
+ type ReplaceReturnType<TFn, TR> = TFn extends (...a: infer A) => any ? (...a: A) => TR : never;
330
+
331
+ /**
332
+ * Works as Partial, but makes only specified keys partial.
333
+ *
334
+ * @since 0.0.1
335
+ * @example
336
+ * type Super = SelectivePartial<{
337
+ * name: string;
338
+ * address: {
339
+ * street: string;
340
+ * number: number
341
+ * }
342
+ * }, 'address'>;
343
+ *
344
+ * // Property 'address' will be partial.
345
+ */
346
+ type SelectivePartial<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
347
+
348
+ /**
349
+ * The strict version of Omit. Allows to remove only
350
+ * keys that are presented in T type.
351
+ *
352
+ * @since 0.0.1
353
+ * @example
354
+ * type Tree = {
355
+ * height: number;
356
+ * age: number;
357
+ * name: string;
358
+ * };
359
+ *
360
+ * // TS2344: Type 'weight' does not satisfy the constraint keyof Tree
361
+ * type TreeInfo = StrictOmit<Tree, 'weight'>;
362
+ *
363
+ * // Works just like usual Omit
364
+ * type CorrectTreeInfo = StrictOmit<Tree, 'name'>;
365
+ */
366
+ type StrictOmit<T, K extends keyof T> = Omit<T, K>;
367
+
368
+ /**
369
+ * Extracts first type from two generics. Is helper type for {@link OneOf} type.
370
+ * @since 0.0.1
371
+ */
372
+ type OnlyFirst<F, S> = F & {
373
+ [Key in keyof Omit<S, keyof F>]?: never;
374
+ };
375
+ /**
376
+ * Chooses only one of types array.
377
+ *
378
+ * @since 0.0.1
379
+ * @example
380
+ * type Email = {
381
+ * person: string;
382
+ * content: string;
383
+ * address: string;
384
+ * };
385
+ *
386
+ * type Mail = {
387
+ * person: string;
388
+ * content: string;
389
+ * postCode: number;
390
+ * };
391
+ *
392
+ * type Letter = OneOf<[Email, Mail]>;
393
+ *
394
+ * // Will error, cause address is not assignable to type never.
395
+ * const email: Letter = {
396
+ * person: 'person',
397
+ * content: 'content',
398
+ * address: '',
399
+ * postCode: 12,
400
+ * };
401
+ */
402
+ type OneOf<TypesArray extends any[], Res = never, AllProperties = MergeTypes<TypesArray>> = TypesArray extends [infer Head, ...infer Rem] ? OneOf<Rem, Res | OnlyFirst<Head, AllProperties>, AllProperties> : Res;
403
+
404
+ /**
405
+ * Merge array of types to one type.
406
+ * @since 0.0.1
407
+ */
408
+ type MergeTypes<TypesArray extends any[], Res = EmptyObject> = TypesArray extends [infer Head, ...infer Rem] ? MergeTypes<Rem, Res & Head> : Res;
409
+
410
+ /**
411
+ * Allows to pass undefined as Omit`s generic. Weakly omits type.
412
+ *
413
+ * @since 0.0.1
414
+ * @example
415
+ * // Will not omit any keys
416
+ * type Lib = WeakOmit<{ bookCount: number; date: string }, undefined>;
417
+ *
418
+ * // Will omit
419
+ * type LibStrong = WeakOmit<{ bookCount: number; date: string }, 'date'>;
420
+ */
421
+ type WeakOmit<Obj extends AnyObject, Keys extends keyof Obj | undefined = undefined> = Keys extends keyof Obj ? Omit<Obj, Keys> : Obj;
422
+
423
+ /**
424
+ * Makes types don`t merging in unions.
425
+ *
426
+ * @since 0.0.1
427
+ * @example
428
+ * type Languages = 'TypeScript' | 'JavaScript' | Lenient<string>;
429
+ * // ^? "TypeScript" | "JavaScript" | string
430
+ */
431
+ type Lenient<T> = T & {};
432
+ /**
433
+ * This type allows any string, but also saves compiler`s autocomplete
434
+ * feature.
435
+ *
436
+ * @since 0.0.1
437
+ * @example
438
+ * type Languages = 'TypeScript' | 'JavaScript';
439
+ *
440
+ * function handleLanguage(lang: LenientAutocomplete<Languages>) {
441
+ * // ^? string (but autocomplete feature is still here)
442
+ * console.log(lang);
443
+ * }
444
+ */
445
+ type LenientAutocomplete<T extends string> = T | Lenient<string>;
446
+
447
+ /**
448
+ * Presents plain object in pretty form.
449
+ */
450
+ type Prettify<T> = {
451
+ [K in keyof T]: T[K];
452
+ } & {};
453
+ /**
454
+ * Presents object in pretty form recursively.
455
+ */
456
+ type PrettifyDeep<T> = {
457
+ [K in keyof T]: T[K] extends AnyObject ? Prettify<PrettifyDeep<T[K]>> : T[K];
458
+ };
459
+
460
+ /**
461
+ * Creates pipelines of functions that will be executed only
462
+ * when you will call whole pipeline.
463
+ *
464
+ * @since 0.0.1
465
+ *
466
+ * @param fn
467
+ *
468
+ * @example
469
+ * const stringToDateAndTime = pipe(Date.parse)
470
+ * .pipe(n => new Date(n))
471
+ * .pipe(d => d.toDateString())
472
+ * .pipe(s => s.split('T'))
473
+ * .pipe(([date, time]) => ({ date, time }));
474
+ *
475
+ * const res = stringToDateAndTime('Jan 1, 2024');
476
+ * // ^? { date: string | undefined; time: string | undefined }
477
+ *
478
+ * @see https://youtu.be/bH61wRMqp-o?si=D-2Az-_dUFBxG9HG&t=429
479
+ */
480
+ declare function pipe<A, B>(fn: (a: A) => B): {
481
+ (a: A): B;
482
+ pipe<C>(fn2: (b: B) => C): {
483
+ (a: A): C;
484
+ pipe<C_1>(fn2: (b: C) => C_1): {
485
+ (a: A): C_1;
486
+ pipe<C_2>(fn2: (b: C_1) => C_2): {
487
+ (a: A): C_2;
488
+ pipe<C_3>(fn2: (b: C_2) => C_3): {
489
+ (a: A): C_3;
490
+ pipe<C_4>(fn2: (b: C_3) => C_4): {
491
+ (a: A): C_4;
492
+ pipe<C_5>(fn2: (b: C_4) => C_5): {
493
+ (a: A): C_5;
494
+ pipe<C_6>(fn2: (b: C_5) => C_6): {
495
+ (a: A): C_6;
496
+ pipe<C_7>(fn2: (b: C_6) => C_7): {
497
+ (a: A): C_7;
498
+ pipe<C_8>(fn2: (b: C_7) => C_8): {
499
+ (a: A): C_8;
500
+ pipe<C_9>(fn2: (b: C_8) => C_9): {
501
+ (a: A): C_9;
502
+ pipe<C_10>(fn2: (b: C_9) => C_10): /*elided*/ any;
503
+ };
504
+ };
505
+ };
506
+ };
507
+ };
508
+ };
509
+ };
510
+ };
511
+ };
512
+ };
513
+ };
514
+
515
+ /**
516
+ * Capitalizes string (makes first letter uppercase).
517
+ * @since 0.0.1
518
+ */
519
+ declare function capitalize<T extends string>(str: T): Capitalize<T>;
520
+ /**
521
+ * Un-capitalizes string (makes first letter lowercase).
522
+ * @since 0.0.1
523
+ */
524
+ declare function uncapitalize<T extends string>(str: T): Uncapitalize<T>;
525
+
526
+ type Preid = 'alpha' | 'beta' | 'rc';
527
+ interface VersionData {
528
+ /** Version in format: (__``x.x.x``__-beta.x) */
529
+ version: Nullable<string>;
530
+ /** Version preid. (x.x.x-__``beta``__.x) */
531
+ preid?: LenientAutocomplete<Preid>;
532
+ /** Number of pre-release. (x.x.x-beta.__``12``__) */
533
+ prerelease?: string;
534
+ }
535
+ /**
536
+ * Parses version with preid.
537
+ *
538
+ * @since 0.0.1
539
+ *
540
+ * @param raw
541
+ *
542
+ * @example
543
+ * const { version, preid } = parseVersion('0.0.0-beta.0');
544
+ * // version = '0.0.0'
545
+ * // preid = 'beta'
546
+ */
547
+ declare const parseVersion: (raw: string) => VersionData;
548
+
549
+ /**
550
+ * Transforms any given string to transliterated variant.
551
+ *
552
+ * @since 0.0.1
553
+ */
554
+ declare const transliterate: transliteration.TransliterateFunction;
555
+
556
+ /**
557
+ * Constraints number with min and max values.
558
+ *
559
+ * @example Min value
560
+ * console.log({
561
+ * ogNum: 1, // 1
562
+ * minmax: minmax(1, [2, undefined]), // 2
563
+ * });
564
+ *
565
+ * @example Max value
566
+ * console.log({
567
+ * ogNum: 1001, // 1001
568
+ * minmax: minmax(1001, [undefined, 900]), // 900
569
+ * });
570
+ *
571
+ * @example Min and max value
572
+ * console.log({
573
+ * ogNum: 600, // 600
574
+ * minmax: minmax(1001, [2, 900]), // 600
575
+ * });
576
+ */
577
+ declare const minmax: (num: number, [min, max]: [min: number | undefined, max: number | undefined]) => number;
578
+
579
+ /**
580
+ * Creates properly type object with jsxDotNotation.
581
+ *
582
+ * @param comp
583
+ * @param rest
584
+ *
585
+ * @example
586
+ * const FlexComp: FC<PropsWithChildren> = ({ children }) => <div>{children}</div>;
587
+ * const FlexRow: FC<PropsWithChildren & { short?: boolean }> = ({ children }) => (
588
+ * <div>{children}</div>
589
+ * );
590
+ * const FlexCol: FC<PropsWithChildren> = ({ children }) => <div>{children}</div>;
591
+ *
592
+ * const Flex = jsxDotNotation(FlexComp, {
593
+ * Row: FlexRow,
594
+ * Col: FlexCol,
595
+ * });
596
+ *
597
+ * const Am = () => {
598
+ * return (
599
+ * <Flex>
600
+ * <Flex.Row short>
601
+ * <Flex.Col></Flex.Col>
602
+ * </Flex.Row>
603
+ * </Flex>
604
+ * );
605
+ * };
606
+ */
607
+ declare function jsxDotNotation<Props = EmptyObject, Rest extends Record<string, FC<any>> = EmptyObject>(comp: FC<Props>, rest: Rest): FC<Props> & Rest;
608
+
609
+ export { capitalize, jsxDotNotation, minmax, parseVersion, pipe, transliterate, uncapitalize };
610
+ export type { AnyFC, AnyObject, ArrayItemType, ArrayType, AsyncFC, AsyncReturnType, AsyncVariableFC, DataAttributes, Defined, EmptyObject, FCProps, FcProps, Fn, FunctionalChildren, InjectDeep, Jsonish, Lenient, LenientAutocomplete, MatchType, MergeTypes, Modify, NextErrorParams, NextParams, NextSearchParams, Nullable, OneOf, OnlyFirst, Preid, Prettify, PrettifyDeep, RecordKey, RecordValue, ReplaceReturnType, SelectivePartial, SetState, StrictOmit, Synchronous, VariableFC, VariableProps, VersionData, WeakOmit, Writeable, WriteableDeep };
package/next/index.cjs ADDED
@@ -0,0 +1,11 @@
1
+ "use strict";const i=require("next/script");function _interopDefaultCompat(t){return t&&typeof t=="object"&&"default"in t?t.default:t}const i__default=_interopDefaultCompat(i),ReactScan=()=>React.createElement("script",{src:"https://unpkg.com/react-scan/dist/auto.global.js",async:!0}),Metrika=({id:t,clickMap:e=!0,trackLinks:a=!0,accurateTrackBounce:c=!0})=>React.createElement(i__default,{id:"metrika-counter",strategy:"afterInteractive"},` (function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
2
+ m[i].l=1*new Date();
3
+ for (var j = 0; j < document.scripts.length; j++) {if (document.scripts[j].src === r) { return; }}
4
+ k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)})
5
+ (window, document, "script", "https://mc.yandex.ru/metrika/tag.js", "ym");
6
+
7
+ ym(${t}, "init", {
8
+ clickmap:${e},
9
+ trackLinks:${a},
10
+ accurateTrackBounce:${c}
11
+ });`);exports.Metrika=Metrika,exports.ReactScan=ReactScan;
@@ -0,0 +1,67 @@
1
+ import { FC } from 'react';
2
+
3
+ /**
4
+ * Adds react-scan to your Next.js application.
5
+ * @constructor
6
+ * @since 0.0.1
7
+ *
8
+ * @example
9
+ * import { ReactScan } from 'xenopomp-essentials/next';
10
+ *
11
+ * export default function RootLayout({
12
+ * children,
13
+ * }: {
14
+ * children: React.ReactNode
15
+ * }) {
16
+ * return (
17
+ * <html lang="en">
18
+ * <head>
19
+ * <ReactScan />
20
+ * </head>
21
+ * <body>{children}</body>
22
+ * </html>
23
+ * )
24
+ *}
25
+ */
26
+ declare const ReactScan: FC<unknown>;
27
+
28
+ interface MetrikaProps {
29
+ /**
30
+ * ID of your Yandex.Metrika`s counter.
31
+ * Locate at dashboard.
32
+ */
33
+ id: number;
34
+ clickMap?: boolean;
35
+ trackLinks?: boolean;
36
+ accurateTrackBounce?: boolean;
37
+ }
38
+
39
+ /**
40
+ * This component allows you to use Yandex.Metrika
41
+ * in your Next.js projects.
42
+ *
43
+ * @since 0.0.1
44
+ *
45
+ * @example
46
+ * import { Metrika } from 'xenopomp-essentials/next';
47
+ *
48
+ * export default function RootLayout({
49
+ * children,
50
+ * }: {
51
+ * children: React.ReactNode
52
+ * }) {
53
+ * return (
54
+ * <html lang="en">
55
+ * <head>
56
+ * <Metrika id={123456789} />
57
+ * </head>
58
+ * <body>{children}</body>
59
+ * </html>
60
+ * )
61
+ * }
62
+ *
63
+ * @see https://metrika.yandex.ru
64
+ */
65
+ declare const Metrika: FC<MetrikaProps>;
66
+
67
+ export { Metrika, ReactScan };
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "xenopomp-essentials",
3
- "version": "0.5.0-canary.4",
3
+ "version": "0.5.0-canary.6",
4
4
  "author": "XenoPOMP <101574433+XenoPOMP@users.noreply.github.com>",
5
5
  "license": "MIT",
6
- "type": "module",
7
6
  "private": false,
7
+ "type": "commonjs",
8
8
  "repository": {
9
9
  "type": "git",
10
10
  "url": "https://github.com/XenoPOMP/xenopomp-essentials-js.git"
@@ -14,15 +14,18 @@
14
14
  },
15
15
  "scripts": {
16
16
  "build": "run-p build:**",
17
+ "postbuild": "cd ./dist ; rimraf **/*.mjs",
17
18
  "build:main": "unbuild --config .config/build.config.ts",
18
19
  "build:packages:cli": "echo CLI",
19
20
  "lint": "run-p lint:**",
20
21
  "lint:code": "eslint",
21
22
  "vitest": "vitest -c ./.config/vitest.config.ts",
23
+ "vitest:intergrity": "vitest -c ./.config/vitest.config.integrity.ts",
22
24
  "coverage": "yarn vitest run --coverage",
23
25
  "test:unit:watch": "yarn vitest",
24
26
  "test:unit:once": "yarn test:unit:watch --run",
25
- "prepare": "husky"
27
+ "prepare": "husky",
28
+ "test:integrity": "yarn build ; yarn vitest:intergrity run"
26
29
  },
27
30
  "main": "./index.mjs",
28
31
  "types": "./index.d.ts",
@@ -40,6 +43,7 @@
40
43
  "type-fest": ">=4",
41
44
  "typescript": ">=5.4",
42
45
  "vitest": "^4",
46
+ "vitest-matchmedia-mock": "^2",
43
47
  "zod": "*"
44
48
  },
45
49
  "dependencies": {
@@ -56,6 +60,7 @@
56
60
  "@vitest/spy": "^4.0.14",
57
61
  "ansi-colors": "^4.1.3",
58
62
  "deepmerge-ts": "^7.1.5",
63
+ "dotenv": "^17.2.3",
59
64
  "eslint": "^9.22.0",
60
65
  "eslint-config-prettier": "^10.1.1",
61
66
  "eslint-plugin-deprecation": "^3.0.0",
@@ -90,6 +95,7 @@
90
95
  "pinst": "^3.0.0",
91
96
  "prettier": "^3.4.2",
92
97
  "react-dom": "^19.2.0",
98
+ "rimraf": "^6.1.2",
93
99
  "tsd": "^0.31.2",
94
100
  "unbuild": "^3.6.1",
95
101
  "vite": "^7.2.4",
@@ -0,0 +1,14 @@
1
+ type PromiseOr<T> = T | Promise<T>;
2
+
3
+ /**
4
+ * Makes type union of type T and undefined
5
+ * @since 0.0.1
6
+ * @deprecated Renamed to {@link Optional}.
7
+ */
8
+ type Undefinable<T> = T | undefined;
9
+ /**
10
+ * Marks type as optional (undefinable).
11
+ */
12
+ type Optional<T> = T | undefined;
13
+
14
+ export type { Optional as O, PromiseOr as P, Undefinable as U };
@@ -0,0 +1 @@
1
+ "use strict";const vitest=require("vitest"),react=require("@testing-library/react"),o=require("ansi-colors"),c=require("vitest-matchmedia-mock");function _interopDefaultCompat(t){return t&&typeof t=="object"&&"default"in t?t.default:t}const o__default=_interopDefaultCompat(o),c__default=_interopDefaultCompat(c),assertNotThrowing=t=>{vitest.expect(()=>t()).not.toThrow()},assertRendering=(...t)=>{assertNotThrowing(()=>react.render(...t))},assertHookRendering=(...t)=>{assertNotThrowing(()=>react.renderHook(...t))},mockEnv=t=>{vitest.vi.stubEnv("CANONICAL_URL","http://localhost:4242"),vitest.vi.stubEnv("IS_PRODUCTION","false"),t&&Object.entries(t).forEach(([e,s])=>vitest.vi.stubEnv(e,s))},clearMocks=()=>{vitest.vi.clearAllMocks(),vitest.vi.resetAllMocks(),vitest.vi.unstubAllEnvs(),vitest.vi.unstubAllGlobals()},mockRouter=()=>{vitest.vi.mock("next/navigation",()=>({useRouter:()=>({push(){}}),usePathname:()=>"/"}))},twApiMock={addBase:vitest.vi.fn(),addComponents:vitest.vi.fn(),addUtilities:vitest.vi.fn(),addVariant:vitest.vi.fn(),config:vitest.vi.fn(),corePlugins:vitest.vi.fn(),e:vitest.vi.fn(),matchComponents:vitest.vi.fn(),matchUtilities:vitest.vi.fn(),matchVariant:vitest.vi.fn(),theme:vitest.vi.fn()};class r{_executionContext;constructor(e){this._executionContext=e}call(...e){console.debug(`${o__default.blue(`[${this._executionContext}]`)} ${o__default.green(`Custom spy is called with these args: ${o__default.blueBright(e.map(s=>`${s}`).join(", "))}`)}`)}}function spyFactory(t){const e=new r(t),s=vitest.vi.spyOn(e,"call");return{expectToBeCalled:(...n)=>vitest.expect(s).toHaveBeenCalledWith(n),expectToBeNotCalled:(...n)=>vitest.expect(s).not.toHaveBeenCalledWith(n),spy:s,callSpy:(...n)=>e.call(n)}}const stubGlobal=(t,e)=>{vitest.vi.stubGlobal(t,e)},p={beforeAll:vitest.beforeAll,afterEach:vitest.afterEach},injectMocks=(t,e="beforeAll")=>{let s;p[e](()=>s=t()),vitest.afterAll(()=>{clearMocks(),s?.()})},injectMatchMediaMock=()=>{const t=new c__default;injectMocks(()=>(t.clear(),()=>t.destroy()),"afterEach")};exports.assertHookRendering=assertHookRendering,exports.assertNotThrowing=assertNotThrowing,exports.assertRendering=assertRendering,exports.clearMocks=clearMocks,exports.injectMatchMediaMock=injectMatchMediaMock,exports.injectMocks=injectMocks,exports.mockEnv=mockEnv,exports.mockRouter=mockRouter,exports.spyFactory=spyFactory,exports.stubGlobal=stubGlobal,exports.twApiMock=twApiMock;
@@ -0,0 +1,176 @@
1
+ import { P as PromiseOr, O as Optional } from '../shared/xenopomp-essentials.CO5lcTlK.cjs';
2
+ import { render, renderHook } from '@testing-library/react';
3
+ import * as vitest from 'vitest';
4
+ import { beforeAll, afterEach, Mock } from 'vitest';
5
+ import * as _vitest_spy from '@vitest/spy';
6
+ import { PartialDeep } from 'type-fest';
7
+
8
+ /** Executes function and expects that it won't throw. */
9
+ declare const assertNotThrowing: <T = void>(callable: () => PromiseOr<T>) => void;
10
+
11
+ type RenderArguments = Parameters<typeof render>;
12
+ type RenderHookArguments = Parameters<typeof renderHook>;
13
+ type RenderFunc<Args extends unknown[] = RenderArguments> = (...args: Args) => void;
14
+ /**
15
+ * Renders component and expects that it won`t throw.
16
+ *
17
+ * @example
18
+ * test('It renders', () => {
19
+ * test('It renders', () => {
20
+ * assertRendering(<MyComponent />, {
21
+ * wrapper: RQProvider,
22
+ * });
23
+ * });
24
+ * });
25
+ *
26
+ */
27
+ declare const assertRendering: RenderFunc;
28
+ /**
29
+ * Renders hook within test React component without having to create that component yourself.
30
+ * Expects that it won't throw error.
31
+ *
32
+ * @param props
33
+ *
34
+ * @example
35
+ * test('It renders', () => {
36
+ * assertHookRendering(() => useExampleStore());
37
+ * });
38
+ */
39
+ declare const assertHookRendering: RenderFunc<RenderHookArguments>;
40
+
41
+ /**
42
+ * Mocks .env file.
43
+ *
44
+ * @param additionalMocks key-value record of env variables that also have to be mocked.
45
+ */
46
+ declare const mockEnv: (additionalMocks?: Record<string, string>) => void;
47
+
48
+ /**
49
+ * Clear all mocks from Vitest.
50
+ */
51
+ declare const clearMocks: () => void;
52
+
53
+ /**
54
+ * Mock **next/navigation** package.
55
+ */
56
+ declare const mockRouter: () => void;
57
+
58
+ declare const twApiMock: {
59
+ addBase: vitest.Mock<_vitest_spy.Procedure>;
60
+ addComponents: vitest.Mock<_vitest_spy.Procedure>;
61
+ addUtilities: vitest.Mock<_vitest_spy.Procedure>;
62
+ addVariant: vitest.Mock<_vitest_spy.Procedure>;
63
+ config: vitest.Mock<_vitest_spy.Procedure>;
64
+ corePlugins: vitest.Mock<_vitest_spy.Procedure>;
65
+ e: vitest.Mock<_vitest_spy.Procedure>;
66
+ matchComponents: vitest.Mock<_vitest_spy.Procedure>;
67
+ matchUtilities: vitest.Mock<_vitest_spy.Procedure>;
68
+ matchVariant: vitest.Mock<_vitest_spy.Procedure>;
69
+ theme: vitest.Mock<_vitest_spy.Procedure>;
70
+ };
71
+
72
+ type Caller = (...args: any[]) => void;
73
+ /**
74
+ * Creates custom spy factory object that is callable.
75
+ * @param context
76
+ * @example
77
+ * const { callSpy, expectToBeCalled, expectToBeNotCalled } =
78
+ * spyFactory('spyFactory test');
79
+ *
80
+ * // Call spy with some args
81
+ * const args: any[] = [1, 'some', 'args', 2];
82
+ * callSpy(...args);
83
+ *
84
+ * // Run expectation func
85
+ * expectToBeCalled(...args);
86
+ * expectToBeNotCalled(2, 2, 7);
87
+ */
88
+ declare function spyFactory(context: string): {
89
+ expectToBeCalled: Caller;
90
+ expectToBeNotCalled: Caller;
91
+ spy: vitest.Mock<(...args: any[]) => void>;
92
+ callSpy: Caller;
93
+ };
94
+
95
+ /**
96
+ * Mocks global scope object.
97
+ *
98
+ * @param name
99
+ * @param stub
100
+ *
101
+ * @example
102
+ * stubGlobal<typeof navigator>('navigator', {
103
+ * clipboard: {
104
+ * writeText: async (text: string) => vi.fn(),
105
+ * },
106
+ * });
107
+ */
108
+ declare const stubGlobal: <Global extends object>(name: string, stub: PartialDeep<Stub<Global>>) => void;
109
+
110
+ type Fn = () => void;
111
+ type Strategy = keyof typeof strategies;
112
+ type AfterAllCallback = Optional<Fn> | void;
113
+ /**
114
+ * Map of all available strategies for
115
+ * injectMocks function.
116
+ */
117
+ declare const strategies: {
118
+ beforeAll: typeof beforeAll;
119
+ afterEach: typeof afterEach;
120
+ };
121
+ /**
122
+ * Inserts mocks according to selected strategy
123
+ * (default is `beforeAll`).
124
+ *
125
+ * This function takes approach of useEffect hook
126
+ * from React. You define mocks inside function body,
127
+ * then you return function, that will be called
128
+ * in `afterAll` event.
129
+ *
130
+ * @example
131
+ * injectMocks(() => {
132
+ * mockRouter();
133
+ *
134
+ * return () => {
135
+ * console.log('This is printed from injectMocks func (afterAll event).');
136
+ * };
137
+ * });
138
+ */
139
+ declare const injectMocks: (func: () => AfterAllCallback, strategy?: Strategy) => void;
140
+
141
+ /**
142
+ * Mocks stuff related to window.matchMedia.
143
+ *
144
+ * @example
145
+ * describe('Root layout test', () => {
146
+ * // Use it inside describe suite. Place it to top
147
+ * injectMatchMediaMock();
148
+ *
149
+ * injectMocks(() => {
150
+ * vi.mock('next/font/google', () => ({
151
+ * Inter: FONT_MOCK,
152
+ * }));
153
+ * });
154
+ *
155
+ * testNextPage(<RootLayout children={undefined} />, {
156
+ * generateMetadata,
157
+ * });
158
+ * });
159
+ */
160
+ declare const injectMatchMediaMock: () => void;
161
+
162
+ /**
163
+ * Recursevely changes all keys` types inside T to R.
164
+ */
165
+ type TypeReplaceDeep<T extends object, R> = {
166
+ [K in keyof T]: T[K] extends object ? TypeReplaceDeep<T[K], R> : R;
167
+ };
168
+
169
+ type StubValue = Mock | (() => Mock) | (() => Promise<Mock>);
170
+ /**
171
+ * Represents vi stub object.
172
+ */
173
+ type Stub<T extends object> = TypeReplaceDeep<T, StubValue>;
174
+
175
+ export { assertHookRendering, assertNotThrowing, assertRendering, clearMocks, injectMatchMediaMock, injectMocks, mockEnv, mockRouter, spyFactory, stubGlobal, twApiMock };
176
+ export type { Stub, TypeReplaceDeep };
@@ -1 +0,0 @@
1
- import s from"node:path";import{z as r}from"zod";const i=r.object({dirname:r.string().optional()}),h=i.parse(import.meta);class a{paths=[];pushPaths(...t){return this.paths.push(...t),this}cd(...t){return this.pushPaths(...t)}file(t){return this.pushPaths(`./${t}`)}appSource(){return h.dirname?this.pushPaths(s.join(s.dirname(h.dirname),"../")):this.cwd()}cwd(){return this.pushPaths(process.cwd())}clear(){return this.paths=[],this}build(){return s.join(...this.paths)}}export{a as PathBuilder};
package/eslint/index.mjs DELETED
@@ -1 +0,0 @@
1
- import{GLOB_JSX as a,GLOB_TSX as i,interopDefault as f,GLOB_MARKDOWN_CODE as p,antfu as c}from"@antfu/eslint-config";import{deepmerge as o}from"deepmerge-ts";import s from"globals";import l from"eslint-plugin-prettier/recommended";import{fixupPluginRules as u}from"@eslint/compat";import m from"eslint-plugin-deprecation";const d="XenoPOMP",t=d.toLowerCase();async function x(){return[{files:[a,i],name:`${t}:react`,rules:{"react-refresh/only-export-components":"off","react/no-clone-element":"off","react/no-missing-component-display-name":"off"}}]}async function y(){const e=await f(import("@next/eslint-plugin-next"));return[{name:`${t}:next`,plugins:{"@next/next":e},rules:{"@next/next/google-font-display":"warn","@next/next/google-font-preconnect":"warn","@next/next/next-script-for-ga":"warn","@next/next/no-async-client-component":"warn","@next/next/no-before-interactive-script-outside-document":"warn","@next/next/no-css-tags":"warn","@next/next/no-head-element":"warn","@next/next/no-html-link-for-pages":"warn","@next/next/no-img-element":"warn","@next/next/no-page-custom-font":"warn","@next/next/no-styled-jsx-in-document":"warn","@next/next/no-sync-scripts":"warn","@next/next/no-title-in-document-head":"warn","@next/next/no-typos":"warn","@next/next/no-unwanted-polyfillio":"warn","@next/next/inline-script-id":"error","@next/next/no-assign-module-variable":"error","@next/next/no-document-import-in-page":"error","@next/next/no-duplicate-head":"error","@next/next/no-head-import-in-document":"error","@next/next/no-script-component-in-head":"error"}}]}async function g(){return[{name:`${t}:markdown`,files:[p],rules:{"import/no-unresolved":"off","unused-imports/no-unused-imports":"off","unused-imports/no-unused-vars":"off","no-alert":"off","no-console":"off","no-restricted-imports":"off","no-undef":"off","no-unused-expressions":"off","no-unused-vars":"off","antfu/no-cjs-exports":"off","antfu/no-ts-export-equal":"off","ts/no-redeclare":"off","ts/no-unused-vars":"off","ts/no-var-requires":"off","ts/consistent-type-imports":"off"}}]}async function w(){return[{name:`${t}:all`,rules:{"antfu/no-cjs-exports":"off","import/prefer-default-export":"off","import/extensions":"off","no-restricted-globals":"off","node/prefer-global/process":"off","jsonc/comma-dangle":"off","style/jsx-quotes":["error","prefer-double"],"style/jsx-one-expression-per-line":["error",{allow:"single-line"}],"style/arrow-parens":["error","as-needed"],"unused-imports/no-unused-imports":"error","unused-imports/no-unused-vars":["warn",{vars:"all",varsIgnorePattern:"^_",args:"after-used",argsIgnorePattern:"^_"}],"@stylistic/indent":["error",2,{ArrayExpression:1,CallExpression:{arguments:1},flatTernaryExpressions:!1,FunctionDeclaration:{body:1,parameters:1},FunctionExpression:{body:1,parameters:1},ignoreComments:!1,ignoredNodes:["TemplateLiteral *","TSIntersectionType","TSTypeParameterInstantiation","FunctionExpression > .params[decorators.length > 0]","FunctionExpression > .params > :matches(Decorator, :not(:first-child))","ClassBody.body > PropertyDefinition[decorators.length > 0] > .key"],ImportDeclaration:1,MemberExpression:1,ObjectExpression:1,offsetTernaryExpressions:!0,outerIIFEBody:1,SwitchCase:1,VariableDeclarator:1}]}}]}async function h(){return[{name:`${t}:old_config`,languageOptions:{globals:{...s.browser,...s.jquery,...s.node,document:"readonly",navigator:"readonly",window:"readonly"}},ignores:["**/.next/*","**/node_modules/*","**/.github/*","cypress","**/__tests__/e2e/*","*.json","**/*.d.ts",".eslintrc.js","eslint.config.js",".prettierrc",".stylelintrc.js","tsconfig.json","package.json","*.md","*.config.ts","*.config.js","*.md"]},{name:`${t}:rules_breakup_1`,rules:{"@next/next/no-duplicate-head":"off"}},{name:`${t}:rules_breakup_2`,rules:{"style/operator-linebreak":"off","test/consistent-test-it":"off","test/prefer-lowercase-title":"off","style/jsx-quotes":"off","style/multiline-ternary":"off","style/indent":"off"},ignores:["cypress"]},{name:`${t}:prettier`,rules:{"antfu/if-newline":"off"}}]}async function j(){return[l]}async function _(e,r){return[{name:"Deprecation plugin",languageOptions:{parserOptions:{project:r?.tsconfigPath??"./tsconfig.json",projectSource:!0}},plugins:{deprecation:u(m)},rules:{"deprecation/deprecation":e},files:["**/*.ts","**/*.tsx"]}]}function b(e,...r){const n=[];return e={...e,react:o(!0,e?.react),vue:o(!1,e?.vue),jsonc:o(!1,e?.jsonc),yaml:o(!1,e?.yaml),stylistic:o({semi:!0,quotes:"single"},e?.stylistic),typescript:o({overrides:{"ts/consistent-type-definitions":["error","interface"],"ts/interface-name-prefix":"off","ts/explicit-function-return-type":"off","ts/explicit-module-boundary-types":"off","ts/no-explicit-any":"off"}},e?.typescript),rules:o({"import/order":"off","react/react-in-jsx-scope":"off","react/prop-types":"off","antfu/top-level-function":"off","perfectionist/sort-imports":"off","perfectionist/sort-named-imports":"off","perfectionist/sort-named-exports":"off","antfu/consistent-chaining":"off","perfectionist/sort-exports":"off","style/no-multiple-empty-lines":"off"},e?.rules),deprecation:e?.deprecation,tsconfigPath:e?.tsconfigPath},(e.react??!0)&&n.push(x()),(e.all??!0)&&n.push(w()),(e.next??!1)&&n.push(y()),(e.markdown??!0)&&n.push(g()),e?.deprecation&&n.push(_(e?.deprecation??"warn",{tsconfigPath:e?.tsconfigPath})),n.push(h()),n.push(j()),c(e,...n,...r)}export{b as default};
package/next/index.mjs DELETED
@@ -1,11 +0,0 @@
1
- import r from"next/script";const n=()=>React.createElement("script",{src:"https://unpkg.com/react-scan/dist/auto.global.js",async:!0}),i=({id:t,clickMap:e=!0,trackLinks:a=!0,accurateTrackBounce:c=!0})=>React.createElement(r,{id:"metrika-counter",strategy:"afterInteractive"},` (function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
2
- m[i].l=1*new Date();
3
- for (var j = 0; j < document.scripts.length; j++) {if (document.scripts[j].src === r) { return; }}
4
- k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)})
5
- (window, document, "script", "https://mc.yandex.ru/metrika/tag.js", "ym");
6
-
7
- ym(${t}, "init", {
8
- clickmap:${e},
9
- trackLinks:${a},
10
- accurateTrackBounce:${c}
11
- });`);export{i as Metrika,n as ReactScan};
package/vitest/index.mjs DELETED
@@ -1 +0,0 @@
1
- import{expect as c,vi as t,afterEach as f,beforeAll as u,afterAll as h}from"vitest";import{render as d,renderHook as m}from"@testing-library/react";import l from"ansi-colors";import p from"vitest-matchmedia-mock";const a=e=>{c(()=>e()).not.toThrow()},b=(...e)=>{a(()=>d(...e))},C=(...e)=>{a(()=>m(...e))},k=e=>{t.stubEnv("CANONICAL_URL","http://localhost:4242"),t.stubEnv("IS_PRODUCTION","false"),e&&Object.entries(e).forEach(([o,n])=>t.stubEnv(o,n))},r=()=>{t.clearAllMocks(),t.resetAllMocks(),t.unstubAllEnvs(),t.unstubAllGlobals()},x=()=>{t.mock("next/navigation",()=>({useRouter:()=>({push(){}}),usePathname:()=>"/"}))},A={addBase:t.fn(),addComponents:t.fn(),addUtilities:t.fn(),addVariant:t.fn(),config:t.fn(),corePlugins:t.fn(),e:t.fn(),matchComponents:t.fn(),matchUtilities:t.fn(),matchVariant:t.fn(),theme:t.fn()};class g{_executionContext;constructor(o){this._executionContext=o}call(...o){console.debug(`${l.blue(`[${this._executionContext}]`)} ${l.green(`Custom spy is called with these args: ${l.blueBright(o.map(n=>`${n}`).join(", "))}`)}`)}}function v(e){const o=new g(e),n=t.spyOn(o,"call");return{expectToBeCalled:(...s)=>c(n).toHaveBeenCalledWith(s),expectToBeNotCalled:(...s)=>c(n).not.toHaveBeenCalledWith(s),spy:n,callSpy:(...s)=>o.call(s)}}const E=(e,o)=>{t.stubGlobal(e,o)},M={beforeAll:u,afterEach:f},i=(e,o="beforeAll")=>{let n;M[o](()=>n=e()),h(()=>{r(),n?.()})},y=()=>{const e=new p;i(()=>(e.clear(),()=>e.destroy()),"afterEach")};export{C as assertHookRendering,a as assertNotThrowing,b as assertRendering,r as clearMocks,y as injectMatchMediaMock,i as injectMocks,k as mockEnv,x as mockRouter,v as spyFactory,E as stubGlobal,A as twApiMock};