hightjs 0.2.42 → 0.2.45

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.
Files changed (76) hide show
  1. package/.idea/copilotDiffState.xml +67 -0
  2. package/README.md +26 -514
  3. package/dist/auth/core.js +3 -3
  4. package/dist/auth/index.d.ts +1 -1
  5. package/dist/auth/index.js +2 -1
  6. package/dist/auth/providers/google.d.ts +63 -0
  7. package/dist/auth/providers/google.js +186 -0
  8. package/dist/auth/providers.d.ts +1 -0
  9. package/dist/auth/providers.js +3 -1
  10. package/dist/auth/types.d.ts +6 -7
  11. package/dist/bin/hightjs.js +393 -0
  12. package/dist/client/entry.client.js +11 -1
  13. package/dist/hotReload.d.ts +8 -1
  14. package/dist/hotReload.js +304 -144
  15. package/dist/index.d.ts +2 -1
  16. package/dist/index.js +20 -33
  17. package/dist/renderer.js +1 -1
  18. package/dist/router.d.ts +24 -1
  19. package/dist/router.js +201 -2
  20. package/dist/types.d.ts +19 -1
  21. package/docs/README.md +59 -0
  22. package/docs/adapters.md +7 -0
  23. package/docs/arquivos-especiais.md +10 -0
  24. package/docs/autenticacao.md +212 -0
  25. package/docs/checklist.md +9 -0
  26. package/docs/cli.md +21 -0
  27. package/docs/estrutura.md +20 -0
  28. package/docs/faq.md +10 -0
  29. package/docs/hot-reload.md +5 -0
  30. package/docs/middlewares.md +73 -0
  31. package/docs/rotas-backend.md +45 -0
  32. package/docs/rotas-frontend.md +66 -0
  33. package/docs/seguranca.md +8 -0
  34. package/docs/websocket.md +45 -0
  35. package/package.json +1 -1
  36. package/src/auth/core.ts +3 -3
  37. package/src/auth/index.ts +2 -3
  38. package/src/auth/providers/google.ts +218 -0
  39. package/src/auth/providers.ts +1 -1
  40. package/src/auth/types.ts +3 -8
  41. package/src/bin/hightjs.js +475 -0
  42. package/src/client/entry.client.tsx +12 -1
  43. package/src/hotReload.ts +333 -147
  44. package/src/index.ts +58 -51
  45. package/src/renderer.tsx +1 -1
  46. package/src/router.ts +230 -3
  47. package/src/types.ts +24 -1
  48. package/dist/adapters/starters/express.d.ts +0 -0
  49. package/dist/adapters/starters/express.js +0 -1
  50. package/dist/adapters/starters/factory.d.ts +0 -0
  51. package/dist/adapters/starters/factory.js +0 -1
  52. package/dist/adapters/starters/fastify.d.ts +0 -0
  53. package/dist/adapters/starters/fastify.js +0 -1
  54. package/dist/adapters/starters/index.d.ts +0 -0
  55. package/dist/adapters/starters/index.js +0 -1
  56. package/dist/adapters/starters/native.d.ts +0 -0
  57. package/dist/adapters/starters/native.js +0 -1
  58. package/dist/auth/example.d.ts +0 -40
  59. package/dist/auth/example.js +0 -104
  60. package/dist/client/ErrorBoundary.d.ts +0 -16
  61. package/dist/client/ErrorBoundary.js +0 -181
  62. package/dist/client/routerContext.d.ts +0 -26
  63. package/dist/client/routerContext.js +0 -62
  64. package/dist/eslint/index.d.ts +0 -32
  65. package/dist/eslint/index.js +0 -15
  66. package/dist/eslint/use-client-rule.d.ts +0 -19
  67. package/dist/eslint/use-client-rule.js +0 -99
  68. package/dist/eslintSetup.d.ts +0 -0
  69. package/dist/eslintSetup.js +0 -1
  70. package/dist/example/src/web/routes/index.d.ts +0 -3
  71. package/dist/example/src/web/routes/index.js +0 -15
  72. package/dist/typescript/use-client-plugin.d.ts +0 -5
  73. package/dist/typescript/use-client-plugin.js +0 -113
  74. package/dist/validation.d.ts +0 -0
  75. package/dist/validation.js +0 -1
  76. package/src/auth/example.ts +0 -115
@@ -1,16 +0,0 @@
1
- import React, { Component, ErrorInfo, ReactNode } from 'react';
2
- interface Props {
3
- children: ReactNode;
4
- }
5
- interface State {
6
- hasError: boolean;
7
- error?: Error;
8
- errorInfo?: ErrorInfo;
9
- }
10
- export declare class ErrorBoundary extends Component<Props, State> {
11
- constructor(props: Props);
12
- static getDerivedStateFromError(error: Error): State;
13
- componentDidCatch(error: Error, errorInfo: ErrorInfo): void;
14
- render(): string | number | boolean | Iterable<React.ReactNode> | import("react/jsx-runtime").JSX.Element | null | undefined;
15
- }
16
- export {};
@@ -1,181 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ErrorBoundary = void 0;
4
- const jsx_runtime_1 = require("react/jsx-runtime");
5
- const react_1 = require("react");
6
- class ErrorBoundary extends react_1.Component {
7
- constructor(props) {
8
- super(props);
9
- this.state = { hasError: false };
10
- }
11
- static getDerivedStateFromError(error) {
12
- // Atualiza o state para que a próxima renderização mostre a UI de erro
13
- return { hasError: true, error };
14
- }
15
- componentDidCatch(error, errorInfo) {
16
- console.error('ErrorBoundary capturou um erro:', error, errorInfo);
17
- this.setState({
18
- error,
19
- errorInfo
20
- });
21
- }
22
- render() {
23
- if (this.state.hasError) {
24
- return (0, jsx_runtime_1.jsx)(ErrorDisplay, { error: this.state.error, errorInfo: this.state.errorInfo });
25
- }
26
- return this.props.children;
27
- }
28
- }
29
- exports.ErrorBoundary = ErrorBoundary;
30
- function ErrorDisplay({ error, errorInfo }) {
31
- const isDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
32
- return ((0, jsx_runtime_1.jsx)("div", { style: {
33
- fontFamily: 'system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif',
34
- height: '100vh',
35
- padding: '20px',
36
- backgroundColor: isDark ? '#0a0a0a' : '#ffffff',
37
- color: isDark ? '#fafafa' : '#171717',
38
- overflow: 'auto'
39
- }, children: (0, jsx_runtime_1.jsxs)("div", { style: {
40
- maxWidth: '800px',
41
- margin: '0 auto',
42
- paddingTop: '40px'
43
- }, children: [(0, jsx_runtime_1.jsxs)("div", { style: {
44
- borderBottom: `1px solid ${isDark ? '#262626' : '#e5e5e5'}`,
45
- paddingBottom: '20px',
46
- marginBottom: '30px'
47
- }, children: [(0, jsx_runtime_1.jsx)("h1", { style: {
48
- fontSize: '24px',
49
- fontWeight: '600',
50
- margin: '0 0 10px 0',
51
- color: '#dc2626'
52
- }, children: "\u274C Erro na Aplica\u00E7\u00E3o" }), (0, jsx_runtime_1.jsx)("p", { style: {
53
- fontSize: '16px',
54
- margin: '0',
55
- color: isDark ? '#a3a3a3' : '#737373'
56
- }, children: "Ocorreu um erro inesperado na aplica\u00E7\u00E3o. Veja os detalhes abaixo:" })] }), (0, jsx_runtime_1.jsxs)("div", { style: {
57
- backgroundColor: isDark ? '#1c1917' : '#fef2f2',
58
- border: `1px solid ${isDark ? '#451a03' : '#fecaca'}`,
59
- borderRadius: '8px',
60
- padding: '20px',
61
- marginBottom: '20px'
62
- }, children: [(0, jsx_runtime_1.jsx)("h2", { style: {
63
- fontSize: '18px',
64
- fontWeight: '600',
65
- margin: '0 0 10px 0',
66
- color: '#dc2626'
67
- }, children: "Mensagem do Erro:" }), (0, jsx_runtime_1.jsx)("p", { style: {
68
- fontSize: '14px',
69
- fontFamily: 'Consolas, Monaco, "Courier New", monospace',
70
- backgroundColor: isDark ? '#0c0a09' : '#ffffff',
71
- padding: '15px',
72
- borderRadius: '6px',
73
- border: `1px solid ${isDark ? '#292524' : '#e5e5e5'}`,
74
- margin: '0',
75
- whiteSpace: 'pre-wrap',
76
- overflow: 'auto'
77
- }, children: error?.message || 'Erro desconhecido' })] }), error?.stack && ((0, jsx_runtime_1.jsxs)("div", { style: {
78
- backgroundColor: isDark ? '#0c0a09' : '#fafafa',
79
- border: `1px solid ${isDark ? '#292524' : '#e5e5e5'}`,
80
- borderRadius: '8px',
81
- padding: '20px',
82
- marginBottom: '20px'
83
- }, children: [(0, jsx_runtime_1.jsx)("h2", { style: {
84
- fontSize: '18px',
85
- fontWeight: '600',
86
- margin: '0 0 10px 0',
87
- color: isDark ? '#fafafa' : '#171717'
88
- }, children: "Stack Trace:" }), (0, jsx_runtime_1.jsx)("pre", { style: {
89
- fontSize: '12px',
90
- fontFamily: 'Consolas, Monaco, "Courier New", monospace',
91
- backgroundColor: isDark ? '#1c1917' : '#ffffff',
92
- padding: '15px',
93
- borderRadius: '6px',
94
- border: `1px solid ${isDark ? '#44403c' : '#d4d4d4'}`,
95
- margin: '0',
96
- overflow: 'auto',
97
- whiteSpace: 'pre-wrap',
98
- color: isDark ? '#e5e5e5' : '#525252'
99
- }, children: error.stack })] })), errorInfo?.componentStack && ((0, jsx_runtime_1.jsxs)("div", { style: {
100
- backgroundColor: isDark ? '#0c0a09' : '#fafafa',
101
- border: `1px solid ${isDark ? '#292524' : '#e5e5e5'}`,
102
- borderRadius: '8px',
103
- padding: '20px',
104
- marginBottom: '20px'
105
- }, children: [(0, jsx_runtime_1.jsx)("h2", { style: {
106
- fontSize: '18px',
107
- fontWeight: '600',
108
- margin: '0 0 10px 0',
109
- color: isDark ? '#fafafa' : '#171717'
110
- }, children: "Component Stack:" }), (0, jsx_runtime_1.jsx)("pre", { style: {
111
- fontSize: '12px',
112
- fontFamily: 'Consolas, Monaco, "Courier New", monospace',
113
- backgroundColor: isDark ? '#1c1917' : '#ffffff',
114
- padding: '15px',
115
- borderRadius: '6px',
116
- border: `1px solid ${isDark ? '#44403c' : '#d4d4d4'}`,
117
- margin: '0',
118
- overflow: 'auto',
119
- whiteSpace: 'pre-wrap',
120
- color: isDark ? '#e5e5e5' : '#525252'
121
- }, children: errorInfo.componentStack })] })), (0, jsx_runtime_1.jsxs)("div", { style: {
122
- display: 'flex',
123
- gap: '10px',
124
- marginTop: '30px'
125
- }, children: [(0, jsx_runtime_1.jsx)("button", { onClick: () => window.location.reload(), style: {
126
- backgroundColor: '#2563eb',
127
- color: '#ffffff',
128
- border: 'none',
129
- padding: '12px 20px',
130
- borderRadius: '6px',
131
- fontSize: '14px',
132
- fontWeight: '500',
133
- cursor: 'pointer',
134
- transition: 'background-color 0.2s'
135
- }, onMouseEnter: (e) => {
136
- e.currentTarget.style.backgroundColor = '#1d4ed8';
137
- }, onMouseLeave: (e) => {
138
- e.currentTarget.style.backgroundColor = '#2563eb';
139
- }, children: "\uD83D\uDD04 Recarregar P\u00E1gina" }), (0, jsx_runtime_1.jsx)("button", { onClick: () => {
140
- navigator.clipboard?.writeText(`
141
- Erro: ${error?.message || 'Erro desconhecido'}
142
-
143
- Stack Trace:
144
- ${error?.stack || 'N/A'}
145
-
146
- Component Stack:
147
- ${errorInfo?.componentStack || 'N/A'}
148
- `.trim());
149
- alert('Detalhes do erro copiados para a área de transferência!');
150
- }, style: {
151
- backgroundColor: isDark ? '#374151' : '#f3f4f6',
152
- color: isDark ? '#fafafa' : '#171717',
153
- border: `1px solid ${isDark ? '#4b5563' : '#d1d5db'}`,
154
- padding: '12px 20px',
155
- borderRadius: '6px',
156
- fontSize: '14px',
157
- fontWeight: '500',
158
- cursor: 'pointer',
159
- transition: 'background-color 0.2s'
160
- }, onMouseEnter: (e) => {
161
- e.currentTarget.style.backgroundColor = isDark ? '#4b5563' : '#e5e7eb';
162
- }, onMouseLeave: (e) => {
163
- e.currentTarget.style.backgroundColor = isDark ? '#374151' : '#f3f4f6';
164
- }, children: "\uD83D\uDCCB Copiar Detalhes" })] }), (0, jsx_runtime_1.jsxs)("div", { style: {
165
- marginTop: '40px',
166
- padding: '20px',
167
- backgroundColor: isDark ? '#1e1b1a' : '#f8fafc',
168
- border: `1px solid ${isDark ? '#3c2e2a' : '#e2e8f0'}`,
169
- borderRadius: '8px'
170
- }, children: [(0, jsx_runtime_1.jsx)("h3", { style: {
171
- fontSize: '16px',
172
- fontWeight: '600',
173
- margin: '0 0 10px 0',
174
- color: isDark ? '#fbbf24' : '#d97706'
175
- }, children: "\uD83D\uDCA1 Para Desenvolvedores:" }), (0, jsx_runtime_1.jsxs)("ul", { style: {
176
- fontSize: '14px',
177
- margin: '0',
178
- paddingLeft: '20px',
179
- color: isDark ? '#d1d5db' : '#4b5563'
180
- }, children: [(0, jsx_runtime_1.jsx)("li", { children: "Verifique o console do navegador para mais detalhes" }), (0, jsx_runtime_1.jsx)("li", { children: "Verifique se todos os arquivos foram buildados corretamente" }), (0, jsx_runtime_1.jsx)("li", { children: "Se for erro 404 em main.js, verifique se o servidor est\u00E1 rodando" }), (0, jsx_runtime_1.jsx)("li", { children: "Use as ferramentas de desenvolvimento do navegador para debugar" })] })] })] }) }));
181
- }
@@ -1,26 +0,0 @@
1
- import React from 'react';
2
- interface RouterContextType {
3
- pathname: string;
4
- query: URLSearchParams;
5
- push: (url: string) => Promise<void>;
6
- replace: (url: string) => Promise<void>;
7
- back: () => void;
8
- forward: () => void;
9
- refresh: () => void;
10
- }
11
- export declare function RouterProvider({ children }: {
12
- children: React.ReactNode;
13
- }): import("react/jsx-runtime").JSX.Element;
14
- /**
15
- * Hook para acessar o router dentro de componentes React
16
- */
17
- export declare function useRouter(): RouterContextType;
18
- /**
19
- * Hook para acessar apenas o pathname atual
20
- */
21
- export declare function usePathname(): string;
22
- /**
23
- * Hook para acessar apenas os query parameters
24
- */
25
- export declare function useSearchParams(): URLSearchParams;
26
- export {};
@@ -1,62 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RouterProvider = RouterProvider;
4
- exports.useRouter = useRouter;
5
- exports.usePathname = usePathname;
6
- exports.useSearchParams = useSearchParams;
7
- const jsx_runtime_1 = require("react/jsx-runtime");
8
- const react_1 = require("react");
9
- const clientRouter_1 = require("./clientRouter");
10
- const RouterContext = (0, react_1.createContext)(null);
11
- function RouterProvider({ children }) {
12
- const [pathname, setPathname] = (0, react_1.useState)(clientRouter_1.router.pathname);
13
- const [query, setQuery] = (0, react_1.useState)(clientRouter_1.router.query);
14
- (0, react_1.useEffect)(() => {
15
- const updateRoute = () => {
16
- setPathname(clientRouter_1.router.pathname);
17
- setQuery(clientRouter_1.router.query);
18
- };
19
- // Subscribe to router changes
20
- const unsubscribe = clientRouter_1.router.subscribe(updateRoute);
21
- // Also listen to browser back/forward
22
- window.addEventListener('popstate', updateRoute);
23
- return () => {
24
- unsubscribe();
25
- window.removeEventListener('popstate', updateRoute);
26
- };
27
- }, []);
28
- const value = {
29
- pathname,
30
- query,
31
- push: clientRouter_1.router.push.bind(clientRouter_1.router),
32
- replace: clientRouter_1.router.replace.bind(clientRouter_1.router),
33
- back: clientRouter_1.router.back.bind(clientRouter_1.router),
34
- forward: clientRouter_1.router.forward.bind(clientRouter_1.router),
35
- refresh: clientRouter_1.router.refresh.bind(clientRouter_1.router),
36
- };
37
- return ((0, jsx_runtime_1.jsx)(RouterContext.Provider, { value: value, children: children }));
38
- }
39
- /**
40
- * Hook para acessar o router dentro de componentes React
41
- */
42
- function useRouter() {
43
- const context = (0, react_1.useContext)(RouterContext);
44
- if (!context) {
45
- throw new Error('useRouter deve ser usado dentro de um RouterProvider');
46
- }
47
- return context;
48
- }
49
- /**
50
- * Hook para acessar apenas o pathname atual
51
- */
52
- function usePathname() {
53
- const { pathname } = useRouter();
54
- return pathname;
55
- }
56
- /**
57
- * Hook para acessar apenas os query parameters
58
- */
59
- function useSearchParams() {
60
- const { query } = useRouter();
61
- return query;
62
- }
@@ -1,32 +0,0 @@
1
- export let rules: {
2
- 'require-use-client': {
3
- meta: {
4
- type: string;
5
- docs: {
6
- description: string;
7
- category: string;
8
- recommended: boolean;
9
- };
10
- fixable: string;
11
- schema: never[];
12
- messages: {
13
- missingUseClient: string;
14
- };
15
- };
16
- create(context: any): {
17
- Program(node: any): void;
18
- ImportDeclaration(node: any): void;
19
- CallExpression(node: any): void;
20
- 'Program:exit'(): void;
21
- };
22
- };
23
- };
24
- export namespace configs {
25
- namespace recommended {
26
- export let plugins: string[];
27
- let rules_1: {
28
- 'hightjs/require-use-client': string;
29
- };
30
- export { rules_1 as rules };
31
- }
32
- }
@@ -1,15 +0,0 @@
1
- "use strict";
2
- const useClientRule = require('./use-client-rule');
3
- module.exports = {
4
- rules: {
5
- 'require-use-client': useClientRule,
6
- },
7
- configs: {
8
- recommended: {
9
- plugins: ['hightjs'],
10
- rules: {
11
- 'hightjs/require-use-client': 'error',
12
- },
13
- },
14
- },
15
- };
@@ -1,19 +0,0 @@
1
- export namespace meta {
2
- let type: string;
3
- namespace docs {
4
- let description: string;
5
- let category: string;
6
- let recommended: boolean;
7
- }
8
- let fixable: string;
9
- let schema: never[];
10
- namespace messages {
11
- let missingUseClient: string;
12
- }
13
- }
14
- export function create(context: any): {
15
- Program(node: any): void;
16
- ImportDeclaration(node: any): void;
17
- CallExpression(node: any): void;
18
- 'Program:exit'(): void;
19
- };
@@ -1,99 +0,0 @@
1
- "use strict";
2
- /**
3
- * ESLint rule: require-use-client
4
- * Força o uso de "use client" quando React ou hooks são importados
5
- */
6
- module.exports = {
7
- meta: {
8
- type: 'problem',
9
- docs: {
10
- description: 'Require "use client" directive when importing React or React hooks',
11
- category: 'Best Practices',
12
- recommended: true,
13
- },
14
- fixable: 'code',
15
- schema: [],
16
- messages: {
17
- missingUseClient: 'Arquivos que importam React ou hooks devem começar com "use client"',
18
- },
19
- },
20
- create(context) {
21
- let hasReactImport = false;
22
- let hasReactHooks = false;
23
- let hasUseClient = false;
24
- let firstNode = null;
25
- // Lista de hooks do React que requerem "use client"
26
- const reactHooks = [
27
- 'useState', 'useEffect', 'useContext', 'useReducer',
28
- 'useCallback', 'useMemo', 'useRef', 'useImperativeHandle',
29
- 'useLayoutEffect', 'useDebugValue', 'useDeferredValue',
30
- 'useTransition', 'useId', 'useSyncExternalStore',
31
- 'useInsertionEffect'
32
- ];
33
- return {
34
- Program(node) {
35
- firstNode = node;
36
- // Verifica se já tem "use client" no início do arquivo
37
- const sourceCode = context.getSourceCode();
38
- const firstToken = sourceCode.getFirstToken(node);
39
- if (firstToken && firstToken.type === 'String') {
40
- const value = firstToken.value;
41
- if (value === '"use client"' || value === "'use client'") {
42
- hasUseClient = true;
43
- }
44
- }
45
- // Também verifica comentários
46
- const comments = sourceCode.getAllComments();
47
- if (comments.length > 0) {
48
- const firstComment = comments[0];
49
- if (firstComment.value.trim() === 'use client') {
50
- hasUseClient = true;
51
- }
52
- }
53
- },
54
- ImportDeclaration(node) {
55
- const source = node.source.value;
56
- // Verifica se importa do React
57
- if (source === 'react' || source === 'react/jsx-runtime') {
58
- hasReactImport = true;
59
- // Verifica se importa hooks específicos
60
- if (node.specifiers) {
61
- for (const specifier of node.specifiers) {
62
- if (specifier.type === 'ImportSpecifier' &&
63
- reactHooks.includes(specifier.imported.name)) {
64
- hasReactHooks = true;
65
- break;
66
- }
67
- }
68
- }
69
- }
70
- },
71
- CallExpression(node) {
72
- // Verifica se usa hooks do React diretamente
73
- if (node.callee.type === 'Identifier' &&
74
- reactHooks.includes(node.callee.name)) {
75
- hasReactHooks = true;
76
- }
77
- // Verifica se usa React.useState, React.useEffect, etc.
78
- if (node.callee.type === 'MemberExpression' &&
79
- node.callee.object.name === 'React' &&
80
- reactHooks.includes(node.callee.property.name)) {
81
- hasReactHooks = true;
82
- }
83
- },
84
- 'Program:exit'() {
85
- // Se importa React ou usa hooks mas não tem "use client"
86
- if ((hasReactImport || hasReactHooks) && !hasUseClient) {
87
- context.report({
88
- node: firstNode,
89
- messageId: 'missingUseClient',
90
- fix(fixer) {
91
- // Auto-fix: adiciona "use client" no início do arquivo
92
- return fixer.insertTextBefore(firstNode, '"use client";\n\n');
93
- }
94
- });
95
- }
96
- }
97
- };
98
- },
99
- };
File without changes
@@ -1 +0,0 @@
1
- "use strict";
@@ -1,3 +0,0 @@
1
- import { RouteConfig } from "../../../../client";
2
- export declare const config: RouteConfig;
3
- export default config;
@@ -1,15 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.config = void 0;
4
- const jsx_runtime_1 = require("react/jsx-runtime");
5
- function Home() {
6
- return ((0, jsx_runtime_1.jsx)("h1", { children: "OL\u00C1 MUNDINHO MEU AMIGUINHO, VAMOS TRANSA?" }));
7
- }
8
- exports.config = {
9
- pattern: '/',
10
- component: Home,
11
- generateMetadata: () => ({
12
- title: 'HightJS | Home'
13
- })
14
- };
15
- exports.default = exports.config;
@@ -1,5 +0,0 @@
1
- /**
2
- * TypeScript Plugin: require-use-client
3
- * Integra a validação "use client" diretamente no TypeScript
4
- */
5
- export default function createPlugin(info: any): any;
@@ -1,113 +0,0 @@
1
- "use strict";
2
- /**
3
- * TypeScript Plugin: require-use-client
4
- * Integra a validação "use client" diretamente no TypeScript
5
- */
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.default = createPlugin;
8
- const defaultConfig = {
9
- enabled: true,
10
- severity: 'error'
11
- };
12
- function isReactImport(node) {
13
- if (!node.moduleSpecifier || node.moduleSpecifier.kind !== 10) { // StringLiteral = 10
14
- return false;
15
- }
16
- const moduleName = node.moduleSpecifier.text;
17
- return moduleName === 'react' || moduleName === 'react/jsx-runtime';
18
- }
19
- function hasReactHooks(node) {
20
- if (!isReactImport(node) || !node.importClause) {
21
- return false;
22
- }
23
- const reactHooks = [
24
- 'useState', 'useEffect', 'useContext', 'useReducer',
25
- 'useCallback', 'useMemo', 'useRef', 'useImperativeHandle',
26
- 'useLayoutEffect', 'useDebugValue', 'useDeferredValue',
27
- 'useTransition', 'useId', 'useSyncExternalStore',
28
- 'useInsertionEffect'
29
- ];
30
- if (node.importClause.namedBindings && node.importClause.namedBindings.kind === 271) { // NamedImports = 271
31
- return node.importClause.namedBindings.elements.some((element) => reactHooks.includes(element.name.text));
32
- }
33
- return false;
34
- }
35
- function hasUseClientDirective(sourceFile) {
36
- // Verifica se a primeira statement é uma expressão "use client"
37
- if (sourceFile.statements.length === 0)
38
- return false;
39
- const firstStatement = sourceFile.statements[0];
40
- if (firstStatement.kind === 233 && // ExpressionStatement = 233
41
- firstStatement.expression.kind === 10) { // StringLiteral = 10
42
- const value = firstStatement.expression.text;
43
- return value === 'use client';
44
- }
45
- return false;
46
- }
47
- function isBackendFile(fileName) {
48
- // Verifica se o arquivo está no diretório backend
49
- return fileName.includes('/backend/') || fileName.includes('\\backend\\');
50
- }
51
- function isFrontendFile(fileName) {
52
- // Verifica se é um arquivo frontend (não backend e dentro de src/web)
53
- const isInWebDir = fileName.includes('/src/web/') || fileName.includes('\\src\\web\\');
54
- const isNotBackend = !isBackendFile(fileName);
55
- const isReactFile = fileName.endsWith('.tsx') || fileName.endsWith('.jsx');
56
- return isInWebDir && isNotBackend && isReactFile;
57
- }
58
- function createDiagnostic(sourceFile, severity, start = 0, length = 1) {
59
- const category = severity === 'error' ? 1 : 0; // Error = 1, Warning = 0
60
- return {
61
- file: sourceFile,
62
- start,
63
- length,
64
- messageText: 'Arquivos que importam React ou hooks devem começar com "use client"',
65
- category,
66
- code: 9001, // Código customizado para o plugin HightJS
67
- source: 'hightjs'
68
- };
69
- }
70
- function createPlugin(info) {
71
- const config = { ...defaultConfig, ...info.config };
72
- if (!config.enabled) {
73
- return info.languageService;
74
- }
75
- const proxy = Object.create(null);
76
- // Proxy todas as funções do language service
77
- for (let k of Object.keys(info.languageService)) {
78
- const x = info.languageService[k];
79
- proxy[k] = (...args) => x.apply(info.languageService, args);
80
- }
81
- // Sobrescreve getSemanticDiagnostics para adicionar nossa validação
82
- proxy.getSemanticDiagnostics = (fileName) => {
83
- const originalDiagnostics = info.languageService.getSemanticDiagnostics(fileName);
84
- // Só valida arquivos frontend
85
- if (!isFrontendFile(fileName)) {
86
- return originalDiagnostics;
87
- }
88
- const sourceFile = info.languageService.getProgram()?.getSourceFile(fileName);
89
- if (!sourceFile) {
90
- return originalDiagnostics;
91
- }
92
- let hasReactImportOrHooks = false;
93
- // Verifica se há imports do React ou hooks
94
- function visitNode(node) {
95
- if (node.kind === 270) { // ImportDeclaration = 270
96
- if (isReactImport(node) || hasReactHooks(node)) {
97
- hasReactImportOrHooks = true;
98
- }
99
- }
100
- node.forEachChild?.(visitNode);
101
- }
102
- visitNode(sourceFile);
103
- // Se usa React mas não tem "use client", adiciona diagnóstico
104
- if (hasReactImportOrHooks && !hasUseClientDirective(sourceFile)) {
105
- const diagnostic = createDiagnostic(sourceFile, config.severity, 0, 1);
106
- return [...originalDiagnostics, diagnostic];
107
- }
108
- return originalDiagnostics;
109
- };
110
- return proxy;
111
- }
112
- // Para compatibilidade com diferentes versões do TypeScript
113
- module.exports = createPlugin;
File without changes
@@ -1 +0,0 @@
1
- "use strict";