import templateBuilder from '@babel/template';
import { addDefault } from '@babel/helper-module-imports';
import { createLineContentsByContent } from '@mometa/fs-handler';
import { sha1 as hash } from 'object-hash';
import * as nps from 'path';
const scopePaths = [nps.resolve(__dirname, '../../'), nps.resolve(__dirname, '../../../webpack')];
export default function babelPluginMometaReactInject(api) {
    const { types: t } = api;
    return {
        name: 'babel-plugin-mometa-react-inject',
        pre(state) {
            this.cache = new WeakSet();
        },
        post(state) {
            this.cache = null;
        },
        visitor: {
            Program: {
                enter(path, state) {
                    if (!this.filename || scopePaths.some((scopePath) => this.filename.startsWith(scopePath))) {
                        return;
                    }
                    const rawCode = this.file.code;
                    const lineContents = createLineContentsByContent(rawCode, { filename: this.filename });
                    const getText = ({ start, end }) => {
                        const arr = lineContents.locateByRange({
                            start,
                            end
                        });
                        return arr
                            .map(({ lineNumber, line, start, end }) => {
                            return line.toString();
                        })
                            .join('\n');
                    };
                    const visitor = {
                        JSXExpressionContainer() {
                            /**
                             * <div>
                             *   {element}
                             * </div>
                             */
                        },
                        JSXElement: {
                            enter(path) {
                                var _a, _b, _c, _d, _e, _f, _g, _h;
                                if (this.cache.has(path.node)) {
                                    return;
                                }
                                const openingElement = path.get('openingElement');
                                if (!openingElement) {
                                    return;
                                }
                                const existingProp = openingElement.node.attributes.find((node) => { var _a; return ((_a = node.name) === null || _a === void 0 ? void 0 : _a.name) === '__mometa'; });
                                if (existingProp) {
                                    return;
                                }
                                const jsxExpContainerPath = path.findParent((pPath) => pPath.isJSXExpressionContainer());
                                const mometaData = Object.assign(Object.assign({}, path.node.loc), { 
                                    // @ts-ignore
                                    name: ((_a = openingElement.get('name')) === null || _a === void 0 ? void 0 : _a.node) && getText((_b = openingElement.get('name')) === null || _b === void 0 ? void 0 : _b.node.loc), text: getText(path.node.loc), filename: this.filename, relativeFilename: nps.relative(state.cwd || '', this.filename), emptyChildren: !((_c = path.node.children) === null || _c === void 0 ? void 0 : _c.length), isFirst: !['JSXElement', 'JSXFragment'].includes(path.parentPath.node.type), selfClosed: !path.node.closingElement, innerStart: openingElement.node.loc.end, innerEnd: (_f = (_e = (_d = path.get('closingElement')) === null || _d === void 0 ? void 0 : _d.node) === null || _e === void 0 ? void 0 : _e.loc) === null || _f === void 0 ? void 0 : _f.start });
                                mometaData.hash = hash(mometaData);
                                if (jsxExpContainerPath) {
                                    const container = Object.assign({ text: getText(jsxExpContainerPath.node.loc) }, jsxExpContainerPath.node.loc);
                                    let isFirstElement = false;
                                    jsxExpContainerPath.traverse({
                                        JSXElement(_path) {
                                            isFirstElement = _path === path;
                                            _path.stop();
                                        }
                                    });
                                    mometaData.container = Object.assign(Object.assign({ isFirstElement }, container), { hash: hash(Object.assign(Object.assign({}, container), { filename: this.filename })) });
                                }
                                /**
                                 * <div>
                                 *   <p>1</p>
                                 *   <p>2</p>
                                 * </div>
                                 *
                                 * <Render>
                                 *   {() => <p>
                                 *     <div>1</div>
                                 *     <div>2</div>
                                 *   </p>}
                                 *   {<p>2</p>}
                                 *   <p>3</p>
                                 *   123
                                 * </Render>
                                 */
                                const parentPath = path.findParent((pPath) => pPath !== path && (pPath.isJSXElement() || pPath.isJSXFragment()));
                                if (parentPath) {
                                    const childrenPath = ((_g = parentPath.get('children')) !== null && _g !== void 0 ? _g : []).filter((p) => p.isJSXElement() ||
                                        p.isJSXExpressionContainer() ||
                                        (p.isJSXText() && !!p.node.extra.raw.trim()));
                                    const index = childrenPath.findIndex((childPath) => {
                                        if (childPath === path)
                                            return true;
                                        let f = false;
                                        childPath.traverse({
                                            JSXElement(_path) {
                                                if (_path === path) {
                                                    f = true;
                                                    _path.stop();
                                                }
                                            }
                                        });
                                        return f;
                                    });
                                    if (index >= 0) {
                                        const prevPath = childrenPath[index - 1];
                                        if (prevPath) {
                                            mometaData.previousSibling = Object.assign(Object.assign({}, prevPath.node.loc), { text: getText(prevPath.node.loc) });
                                        }
                                        const nextPath = childrenPath[index + 1];
                                        if (nextPath) {
                                            mometaData.nextSibling = Object.assign(Object.assign({}, nextPath.node.loc), { text: getText(nextPath.node.loc) });
                                        }
                                    }
                                }
                                const objExp = templateBuilder.expression(JSON.stringify(mometaData))();
                                const newProp = t.JSXAttribute(t.JSXIdentifier('__mometa'), t.JSXExpressionContainer(objExp));
                                openingElement.node.attributes.push(newProp);
                                if (!((_h = path.node.children) === null || _h === void 0 ? void 0 : _h.length) && path.node.closingElement) {
                                    const emptyChildrenPlc = this.emptyChildrenPlc ||
                                        (this.emptyChildrenPlc = addDefault(path, state.opts.emptyPlaceholderPath ||
                                            (!!process.env.__MOMETA_LOCAL__
                                                ? require.resolve('../../mometa/runtime/empty-placeholder')
                                                : require.resolve('../../../build/runtime/empty-placeholder')), {
                                            nameHint: 'MometaEmptyPlaceholder'
                                        }));
                                    const childNode = templateBuilder.expression(`<${emptyChildrenPlc.name} />`, {
                                        plugins: ['jsx']
                                    })();
                                    childNode.openingElement.attributes.push(t.cloneDeep(newProp));
                                    this.cache.add(childNode);
                                    path.node.children.push(childNode);
                                }
                            }
                        }
                    };
                    path.traverse(visitor, state);
                }
            }
        }
    };
}
//# sourceMappingURL=index.js.map