1- import type { NodePath , PluginObj , PluginPass } from '@babel/core' ;
1+ import type { NodePath , PluginObj } from '@babel/core' ;
22import { generate } from '@babel/generator' ;
33import * as t from '@babel/types' ;
4- import type { TracingAPI } from '@ottrelite/core' ;
54
65import {
76 CORE_PACKAGE_NAME ,
87 TRACE_COMPONENT_LIFECYCLE_HOC_NAME ,
98 TRACE_COMPONENT_LIFECYCLE_HOOK_NAME ,
10- UNKNOWN_COMPONENT_NAME_PLACEHOLDER ,
119} from './constants' ;
12- import { isEnvBoolVarTrue , uniqueVarName } from './utils' ;
10+ import { getLocationDescription } from './utils/getLocationDescription' ;
11+ import { isEnvBoolVarTrue , uniqueVarName } from './utils/misc' ;
12+ import { parseDirective } from './utils/parseDirective' ;
1313
1414const LOG_DEBUG = isEnvBoolVarTrue ( 'DEBUG' ) ;
1515const PRINT_GENERATED_CODE = isEnvBoolVarTrue ( 'PRINT_GENERATED_CODE' ) ;
@@ -20,124 +20,6 @@ type AugmentedProgramNode = NodePath<t.Program> & {
2020 __hasFunctionComponent : boolean ;
2121} ;
2222
23- function parseDirective (
24- path : NodePath < t . Function | t . Class > ,
25- traceDirective : t . Directive ,
26- locationDescription : string
27- ) {
28- // extract the component name from "use trace ..."
29- const maybeMatches = traceDirective . value . value
30- . match ( / u s e t r a c e \s + ( \S + ) (?: \s + ( \S + ) ) ? $ / )
31- ?. filter ( ( x ) => x !== undefined ) ;
32-
33- let componentName : string | undefined ;
34- let apiToUse : TracingAPI = 'dev' ;
35-
36- if ( maybeMatches ) {
37- // case 1: 'use trace <api/name>'; give priority to api (if valid) over name
38- if ( maybeMatches . length === 2 ) {
39- const apiName = maybeMatches [ 1 ] ?. trim ( ) ;
40-
41- if ( apiName === 'dev' || apiName === 'otel' ) {
42- apiToUse = apiName ;
43- } else {
44- componentName = apiName ;
45- }
46- }
47-
48- // case 2: 'use trace <api> <name>'
49- if ( maybeMatches . length === 3 ) {
50- const apiName = maybeMatches [ 1 ] ?. trim ( ) ;
51- componentName = maybeMatches [ 2 ] ?. trim ( ) ;
52-
53- if ( apiName === 'dev' || apiName === 'otel' ) {
54- apiToUse = apiName ;
55- } else {
56- throw new Error (
57- `[Ottrelite] Invalid tracing API specified: "${ apiName } ". Supported APIs are "dev" and "otel". Localization: ${ locationDescription } .`
58- ) ;
59- }
60- }
61- }
62-
63- let isFunctionComponent = true ;
64-
65- if ( 'id' in path . node && path . node . id ?. name ) {
66- // function or class declaration
67- componentName ??= path . node . id . name ;
68- } else if (
69- path . parent . type === 'VariableDeclarator' &&
70- path . parent . id . type === 'Identifier'
71- ) {
72- // variable assignment
73- componentName ??= path . parent . id . name ;
74- } else if (
75- path . parent . type === 'ObjectProperty' &&
76- path . parent . key . type === 'Identifier'
77- ) {
78- // object property assignment
79- componentName ??= path . parent . key . name ;
80- } else if (
81- path . parent . type === 'ClassMethod' &&
82- path . parent . key . type === 'Identifier'
83- ) {
84- // class method
85- isFunctionComponent = false ;
86- componentName ??= path . parent . key . name ;
87- } else if ( path . parent . type === 'ExportDefaultDeclaration' ) {
88- // anonymous export default
89- throw new Error (
90- `[Ottrelite] Identifier is an anonymous default export, which is not supported! The tracer must be given a unique name. Localization: ${ locationDescription } .`
91- ) ;
92- }
93-
94- // /**
95- // * important notice: the above works for ES6, but if the code passed to Babel is already ES5, then it will look like the following:
96- // *
97- // * return _createClass(ClassComponentName, [{
98- // * key: "render",
99- // * value: function render() {
100- // * ...
101- // * },
102- // * ...
103- // * ]);
104- // *
105- // * to handle this properly, we need to check if there exists a _createClass parent
106- // */
107- // const maybeClassCreationExpression = path.findParent(
108- // (p) =>
109- // t.isCallExpression(p.node) &&
110- // t.isIdentifier(p.node.callee, { name: '_createClass' })
111- // );
112- // if (maybeClassCreationExpression) {
113- // isFunctionComponent = false;
114- // // so far, the componentName would be 'render'
115- // try {
116- // componentName = (
117- // (maybeClassCreationExpression.node as t.CallExpression)
118- // .arguments[0] as t.Identifier
119- // ).name;
120- // } catch {
121- // // it's not critical if a more precise name is impossible to extract
122- // }
123- // }
124-
125- return {
126- componentName : componentName ?? UNKNOWN_COMPONENT_NAME_PLACEHOLDER ,
127- isFunctionComponent,
128- apiToUse,
129- } ;
130- }
131-
132- function getLocationDescription (
133- path : NodePath | t . ClassMethod ,
134- pluginPass : PluginPass
135- ) {
136- const loc =
137- path . type === 'ClassMethod' ? ( path as t . ClassMethod ) . loc : path . node . loc ;
138- return `${ pluginPass . filename } :${ loc ?. start . line } :${ loc ?. start . column } through ${ loc ?. end . line } :${ loc ?. end . column } ` ;
139- }
140-
14123export default function ottrelitePlugin ( ) : PluginObj {
14224 return {
14325 visitor : {
0 commit comments