Skip to content

Commit

Permalink
Update setup to match new packages
Browse files Browse the repository at this point in the history
  • Loading branch information
sergiodxa committed Mar 21, 2024
1 parent ce5f2dd commit 0bd18a7
Show file tree
Hide file tree
Showing 15 changed files with 776 additions and 540 deletions.
54 changes: 54 additions & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/* eslint-disable unicorn/prefer-module */
module.exports = {
root: true,
parser: "@typescript-eslint/parser",
plugins: [
"@typescript-eslint",
"import",
"jsx-a11y",
"prettier",
"promise",
"react-hooks",
"react",
"unicorn",
],
extends: [
"plugin:@typescript-eslint/recommended",
"plugin:import/errors",
"plugin:import/typescript",
"plugin:import/warnings",
"plugin:jsx-a11y/recommended",
"plugin:prettier/recommended",
"plugin:promise/recommended",
"plugin:react-hooks/recommended",
"plugin:react/recommended",
"plugin:unicorn/recommended",
],
settings: {
react: { version: "detect" },
"import/resolver": { typescript: {} },
},
rules: {
"@typescript-eslint/explicit-module-boundary-types": "off",
"no-unused-vars": "off",
"no-var": "off",
"prefer-const": "off",
"react/prop-types": "off",
"react/button-has-type": "error",
"react/function-component-definition": [
"error",
{
namedComponents: "function-declaration",
unnamedComponents: "arrow-function",
},
],
"react/jsx-uses-react": "off",
"react/react-in-jsx-scope": "off",
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "error",
"unicorn/filename-case": "off",
"unicorn/no-null": "off",
"unicorn/prefer-node-protocol": "off",
"unicorn/prevent-abbreviations": "off",
},
};
34 changes: 34 additions & 0 deletions build/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import * as React from "react";
type FeatureGroup = {
[featureName: string]: boolean | FeatureGroup;
};
export type FeatureFlags = string[] | FeatureGroup;
export declare function FlagsProvider({
features,
children,
}: {
features?: FeatureFlags;
children: React.ReactNode;
}): React.JSX.Element;
export declare function useFeatures(): FeatureGroup;
export declare function useFeature(name: string): boolean | FeatureGroup;
export declare function Feature({
name,
children,
render,
}: {
name: string;
children?:
| React.ReactNode
| ((hasFeature: boolean | FeatureGroup) => JSX.Element);
render?:
| React.ReactNode
| ((hasFeature: boolean | FeatureGroup) => JSX.Element);
}): React.JSX.Element | null;
export declare function withFeature<Props extends object>(
featureName: string,
): (Component: React.ComponentType<Props>) => {
(props: Props): React.JSX.Element;
displayName: string;
};
export {};
63 changes: 63 additions & 0 deletions build/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import * as React from "react";
const FeatureFlagsContext = React.createContext({});
function transformFlags(features) {
if (!Array.isArray(features)) return features;
return Object.fromEntries(features.map((feature) => [feature, true]));
}
function mergeFeatures(a, b) {
return { ...a, ...b };
}
export function FlagsProvider({ features = {}, children }) {
const currentFeatures = useFeatures();
return React.createElement(
FeatureFlagsContext.Provider,
{
value: mergeFeatures(
transformFlags(currentFeatures),
transformFlags(features),
),
},
children,
);
}
// Custom Hook API
export function useFeatures() {
return React.useContext(FeatureFlagsContext);
}
// Custom Hook API
export function useFeature(name) {
const features = useFeatures();
if (Array.isArray(features)) return features.includes(name);
if (typeof features[name] === "boolean") return features[name];
return (
name
.split("/")
// eslint-disable-next-line unicorn/no-array-reduce
.reduce((featureGroup, featureName) => {
if (typeof featureGroup === "boolean") return featureGroup;
if (featureGroup[featureName] === undefined) return false;
return featureGroup[featureName];
}, features)
);
}
// Render Prop API
export function Feature({ name, children, render = children }) {
const hasFeature = useFeature(name);
if (typeof render === "function") return render(hasFeature);
if (!hasFeature) return null;
return React.createElement(React.Fragment, null, render);
}
// High Order Component API
export function withFeature(featureName) {
return (Component) => {
function WithFeature(props) {
return React.createElement(
Feature,
{ name: featureName },
React.createElement(Component, { ...props }),
);
}
WithFeature.displayName = `WithFeature(${Component.displayName || Component.name})`;
return WithFeature;
};
}
Binary file modified bun.lockb
Binary file not shown.
89 changes: 53 additions & 36 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,39 @@
"description": "Feature flags for React made easy with hooks, HOC and Render Props",
"version": "2.0.10",
"license": "MIT",
"repository": "https://github.com/sergiodxa/flagged.git",
"engines": {
"node": ">=18.0.0"
},
"type": "module",
"sideEffects": false,
"exports": {
"./package.json": "./package.json",
".": "./build/index.js"
},
"scripts": {
"prepare": "npm run build",
"build": "tsc --project tsconfig.json --outDir ./build",
"postbuild": "prettier --write \"build/**/*.js\" \"build/**/*.d.ts\"",
"format": "prettier --write \"src/**/*.ts\" \"src/**/*.tsx\" \"test/**/*.ts\" \"test/**/*.tsx\" \"*.md\" \"package.json\"",
"typecheck": "tsc --project tsconfig.json --noEmit",
"lint": "eslint --ext .ts,.tsx src/",
"test": "vitest --run",
"test:watch": "vitest",
"test:coverage": "vitest --coverage",
"test:exports": "bun scripts/check-pkg-exports.ts"
},
"homepage": "https://github.com/sergiodxa/flagged",
"author": {
"name": "Sergio Xalambrí",
"url": "https://sergiodxa.com",
"email": "[email protected]"
},
"repository": {
"type": "git",
"url": "https://github.com/sergiodxa/flagged.git"
},
"bugs": {
"email": "[email protected]",
"url": "https://github.com/sergiodxa/flagged"
"url": "https://github.com/sergiodxa/flagged/issues"
},
"main": "dist/index.js",
"module": "dist/flagged.esm.js",
Expand All @@ -32,43 +55,37 @@
"typescript",
"types"
],
"scripts": {
"prepublishOnly": "tsdx build",
"start": "tsdx watch",
"build": "tsdx build",
"test": "vitest",
"lint": "tsdx lint",
"typecheck": "tsc --noEmit"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
},
"husky": {
"hooks": {
"pre-commit": "tsdx lint"
}
},
"prettier": {
"printWidth": 80,
"semi": true,
"singleQuote": true,
"trailingComma": "es5",
"arrowParens": "avoid"
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
},
"devDependencies": {
"@testing-library/jest-dom": "^5.16.4",
"@arethetypeswrong/cli": "^0.13.1",
"@testing-library/jest-dom": "^6.4.2",
"@testing-library/react": "^14.0.0",
"@types/react": "^18.0.10",
"@types/react-dom": "^18.0.5",
"@vitest/coverage-v8": "^1.0.1",
"happy-dom": "^14.0.0",
"husky": "^9.0.10",
"react": "^18.1.0",
"react-dom": "^18.1.0",
"tsdx": "^0.14.1",
"tslib": "^2.4.0",
"typescript": "^4.7.2",
"@types/bun": "^1.0.10",
"@types/react": "^18.2.25",
"@typescript-eslint/eslint-plugin": "^6.7.4",
"@typescript-eslint/parser": "^6.7.4",
"@vitejs/plugin-react": "^4.1.0",
"@vitest/coverage-v8": "^0.34.6",
"eslint": "^8.12.0",
"eslint-config-prettier": "^9.0.0",
"eslint-import-resolver-typescript": "^3.6.1",
"eslint-plugin-cypress": "^2.15.1",
"eslint-plugin-import": "^2.28.1",
"eslint-plugin-jest-dom": "^5.1.0",
"eslint-plugin-jsx-a11y": "^6.7.1",
"eslint-plugin-prettier": "^5.0.0",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-testing-library": "^6.0.2",
"eslint-plugin-unicorn": "^49.0.0",
"happy-dom": "^12.9.0",
"prettier": "^3.2.5",
"react": "^18.0.0",
"typescript": "^5.2.2",
"vite": "^5.0.0",
"vitest": "^1.0.1"
"vitest": "^1.0.0"
}
}
6 changes: 6 additions & 0 deletions prettier.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/* eslint-disable unicorn/prefer-module */
/** @type {import("prettier").Config} */
export default {
trailingComma: "all",
useTabs: true,
};
63 changes: 63 additions & 0 deletions scripts/check-pkg-exports.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/* eslint-disable unicorn/no-process-exit */

async function main() {
const proc = Bun.spawn([
"bunx",
"attw",
"-f",
"table-flipped",
"--no-emoji",
"--no-color",
"--pack",
]);

const text = await new Response(proc.stdout).text();

const entrypointLines = text
.slice(text.indexOf('"remix-utils/'))
.split("\n")
.filter(Boolean)
.filter((line) => !line.includes("─"))
.map((line) =>
line
.replaceAll(/[^\d "()/A-Za-z│-]/g, "")
.replaceAll("90m│39m", "│")
.replaceAll(/^│/g, "")
.replaceAll(/│$/g, ""),
);

const pkg = await Bun.file("package.json").json();
const entrypoints = entrypointLines.map((entrypointLine) => {
const [entrypoint, ...resolutionColumns] = entrypointLine.split("│");
return {
entrypoint: entrypoint.replace(pkg.name, ".").trim(),
esm: resolutionColumns[2].trim(),
bundler: resolutionColumns[3].trim(),
};
});

const entrypointsWithProblems = entrypoints.filter(
(item) => item.esm.includes("fail") || item.bundler.includes("fail"),
);
if (entrypointsWithProblems.length > 0) {
console.error("Entrypoints with problems:");
console.log(
`---\n${entrypointsWithProblems
.map(
({ entrypoint, esm, bundler }) =>
`entrypoint: ${entrypoint}\nesm: ${esm}\nbundler: ${bundler}`,
)
.join("\n---\n")}\n---`,
);
process.exit(1);
} else {
console.log("No problems found.");
}
}

await main().catch((error) => {
console.error(error);
process.exit(1);
});

export {};
Loading

0 comments on commit 0bd18a7

Please sign in to comment.