<commit_before>import { parseScript } from 'esprima';
import { decorateWithComments } from "./commentDecorator";
import {
    createReactColDefSnippet,
    decreaseIndent,
    getName,
    getReactValue,
    isLiteralProperty,
    isProperty,
    tab,
} from "./snippetUtils";

export const transform = (snippet, framework, options) => {
    return {
        'javascript': () => new JavascriptTransformer(snippet, options).transform(),
        'angular': () => new AngularTransformer(snippet, options).transform(),
        'react': () => new ReactTransformer(snippet, options).transform(),
        'vue': () => new VueTransformer(snippet, options).transform(),
    }[framework]();
}

// The SnippetTransformer is based around the 'Template Method' design pattern
class SnippetTransformer {
    initialDepth = 0;

    constructor(snippet, options) {
        this.options = options;
        this.snippet = snippet;
    }

    transform() {
        // create a syntax tree from the supplied snippet
        const treeWithoutAssociatedComments = parseScript(this.snippet, {comment: true, loc: true, range: true});

        // associate comments with each node
        const tree = decorateWithComments(treeWithoutAssociatedComments);

        // parse syntax tree to produce main snippet body
        const snippetBody = this.parse(tree, this.initialDepth);

        // wrap snippet body with framework context
        return this.addFrameworkContext(snippetBody);
    }

    parse(tree, depth) {
        if (Array.isArray(tree)) {
            return tree.map(node => this.parse(node, depth + 1)).join('');
        } else if(isProperty(tree)) {
            return this.addComment(tree, depth) + this.parseProperty(tree, depth);
        } else {
            console.error('Unexpected node type in provided snippet!')
        }
    }
}

class JavascriptTransformer extends SnippetTransformer {
    constructor(snippet, options) {
        super(snippet, options);
        if (this.options.suppressFrameworkContext) {
            this.initialDepth--;
        }
    }

    parseProperty(property, depth) {
        const [start, end] = property.range;
        if (this.options.suppressFrameworkContext) {
            return decreaseIndent(`${this.snippet.slice(start, end)}`) + ',';
        }
        return `${tab(depth)}${this.snippet.slice(start, end)},`;
    }

    addFrameworkContext(result) {
        if (this.options.suppressFrameworkContext) { return result.trim(); }
        return `const gridOptions = {${result}` +
               `\n\n${tab(1)}// other grid options here...` +
               '\n}';
    }

    addComment(property, depth) {
        return property.comment ? `\n${tab(depth)}//${property.comment}\n` : '\n';
    }
}

class AngularTransformer extends SnippetTransformer {
    // used when adding framework context
    propertiesVisited = [];

    parseProperty(property) {
        const propertyName = getName(property);
        this.propertiesVisited.push(propertyName);
        const [start, end] = property.range;
        return decreaseIndent(`${this.snippet.slice(start, end)}`)
            .replace(`${propertyName}:`, `this.${propertyName} =`) + ';';
    }

    addFrameworkContext(result) {
        if (this.options.suppressFrameworkContext) { return result.trim(); }
        const props = this.propertiesVisited.map(property => `${tab(1)}[${property}]="${property}"`).join('\n');
        return '<ag-grid-angular\n' + props +
            '\n    // other grid options ...>\n' +
            '</ag-grid-angular>\n' +
            result;
    }

    addComment(property, depth) {
        return property.comment ? `\n${tab(depth-1)}//${property.comment}\n` : '\n';
    }
}

class VueTransformer extends AngularTransformer {
    addFrameworkContext(result) {
        if (this.options.suppressFrameworkContext) { return result.trim(); }
        const props = this.propertiesVisited.map(property => `${tab(1)}[${property}]="${property}"`).join('\n');
        return '<ag-grid-vue\n' + props +
            '\n    // other grid options ...>\n' +
            '</ag-grid-vue>\n' +
            result;
    }
}

class ReactTransformer extends SnippetTransformer {
    propertySnippets = [];

    extractRawProperty(property, depth) {
        if (isLiteralProperty(property)) {
            let res = property.comment ? `\n${tab(depth)}//${property.comment}` : '';
            return res + `\n${tab(depth)}${getName(property)}=${getReactValue(property)}`;
        }
        const [start, end] = property.range;
        return `${this.snippet.slice(start, end)}`.replace(`${getName(property)}:`, '').trim();
    }

    parseProperty(property, depth) {
        // special handling required for react column definitions
        if (getName(property) === 'columnDefs') {
            let res = property.comment ? `\n${tab(1)}//${property.comment}` : '';
            return createReactColDefSnippet(property.value.elements, depth);
        }
        if (isLiteralProperty(property)) {
            this.propertySnippets.push(this.extractRawProperty(property, depth));
        } else {
            let res = property.comment ? `\n${tab(depth)}//${property.comment}` : '';
            res += `\n${tab(depth)}${getName(property)}={` + this.extractRawProperty(property, depth) + '}';
            this.propertySnippets.push(res);
        }
        return ''; // react grid options are gathered and added later in the framework context
    }

    addFrameworkContext(result) {
        if (this.propertySnippets.length > 0) {
            return `<AgGridReact` +
                   `${this.propertySnippets.join('')}` +
                   `\n${tab(1)}// other grid options ...\n>` +
                   `${result}\n</AgGridReact>`;
        }
        if (this.options.suppressFrameworkContext) {
            // framework context is only hidden if no properties exist for React
            return decreaseIndent(result.trim());
        }
        return `<AgGridReact>${result}\n</AgGridReact>`;
    }

    addComment() {
        return ''; // react comments are added inplace
    }
}
<commit_msg>AG-4934 - minor code cleanup to snippet component<commit_after>import { parseScript } from 'esprima';
import { decorateWithComments } from "./commentDecorator";
import {
    createReactColDefSnippet,
    decreaseIndent,
    getName,
    getReactValue,
    isLiteralProperty,
    isProperty,
    tab,
} from "./snippetUtils";

export const transform = (snippet, framework, options) => {
    return {
        'javascript': () => new JavascriptTransformer(snippet, options).transform(),
        'angular': () => new AngularTransformer(snippet, options).transform(),
        'react': () => new ReactTransformer(snippet, options).transform(),
        'vue': () => new VueTransformer(snippet, options).transform(),
    }[framework]();
}

// The SnippetTransformer is based around the 'Template Method' design pattern
class SnippetTransformer {
    initialDepth = 0;

    constructor(snippet, options) {
        this.options = options;
        this.snippet = snippet;
    }

    transform() {
        // create a syntax tree from the supplied snippet
        const treeWithoutAssociatedComments = parseScript(this.snippet, {comment: true, loc: true, range: true});

        // associate comments with each node
        const tree = decorateWithComments(treeWithoutAssociatedComments);

        // parse syntax tree to produce main snippet body
        const snippetBody = this.parse(tree, this.initialDepth);

        // wrap snippet body with framework context
        return this.addFrameworkContext(snippetBody);
    }

    parse(tree, depth) {
        if (Array.isArray(tree)) {
            return tree.map(node => this.parse(node, depth + 1)).join('');
        } else if(isProperty(tree)) {
            return this.addComment(tree, depth) + this.parseProperty(tree, depth);
        } else {
            console.error('Unexpected node type in provided snippet!')
        }
    }
}

class JavascriptTransformer extends SnippetTransformer {
    constructor(snippet, options) {
        super(snippet, options);
        if (this.options.suppressFrameworkContext) {
            this.initialDepth--;
        }
    }

    parseProperty(property, depth) {
        const [start, end] = property.range;
        if (this.options.suppressFrameworkContext) {
            return decreaseIndent(`${this.snippet.slice(start, end)}`) + ',';
        }
        return `${tab(depth)}${this.snippet.slice(start, end)},`;
    }

    addFrameworkContext(result) {
        if (this.options.suppressFrameworkContext) { return result.trim(); }
        return `const gridOptions = {${result}` +
               `\n\n${tab(1)}// other grid options here...` +
               '\n}';
    }

    addComment(property, depth) {
        return property.comment ? `\n${tab(depth)}//${property.comment}\n` : '\n';
    }
}

class AngularTransformer extends SnippetTransformer {
    // used when adding framework context
    propertiesVisited = [];

    parseProperty(property) {
        const propertyName = getName(property);
        this.propertiesVisited.push(propertyName);
        const [start, end] = property.range;
        return decreaseIndent(`${this.snippet.slice(start, end)}`)
            .replace(`${propertyName}:`, `this.${propertyName} =`) + ';';
    }

    addFrameworkContext(result) {
        if (this.options.suppressFrameworkContext) { return result.trim(); }
        const props = this.propertiesVisited.map(property => `${tab(1)}[${property}]="${property}"`).join('\n');
        return '<ag-grid-angular\n' + props +
            '\n    // other grid options ...>\n' +
            '</ag-grid-angular>\n' +
            result;
    }

    addComment(property, depth) {
        return property.comment ? `\n${tab(depth-1)}//${property.comment}\n` : '\n';
    }
}

class VueTransformer extends AngularTransformer {
    addFrameworkContext(result) {
        if (this.options.suppressFrameworkContext) { return result.trim(); }
        const props = this.propertiesVisited.map(property => `${tab(1)}[${property}]="${property}"`).join('\n');
        return '<ag-grid-vue\n' + props +
            '\n    // other grid options ...>\n' +
            '</ag-grid-vue>\n' +
            result;
    }
}

class ReactTransformer extends SnippetTransformer {
    propertySnippets = [];

    extractRawProperty(property, depth) {
        if (isLiteralProperty(property)) {
            return (property.comment ? `\n${tab(depth)}//${property.comment}` : '') +
                `\n${tab(depth)}${getName(property)}=${getReactValue(property)}`;
        }
        const [start, end] = property.range;
        return `${this.snippet.slice(start, end)}`.replace(`${getName(property)}:`, '').trim();
    }

    parseProperty(property, depth) {
        // special handling required for react column definitions
        if (getName(property) === 'columnDefs') {
            return (property.comment ? `\n${tab(1)}//${property.comment}` : '') +
                createReactColDefSnippet(property.value.elements, depth);
        }
        if (isLiteralProperty(property)) {
            this.propertySnippets.push(this.extractRawProperty(property, depth));
        } else {
            let res = (property.comment ? `\n${tab(depth)}//${property.comment}` : '') +
                `\n${tab(depth)}${getName(property)}={` + this.extractRawProperty(property, depth) + '}';
            this.propertySnippets.push(res);
        }
        return ''; // react grid options are gathered and added later in the framework context
    }

    addFrameworkContext(result) {
        if (this.propertySnippets.length > 0) {
            return `<AgGridReact` +
                   `${this.propertySnippets.join('')}` +
                   `\n${tab(1)}// other grid options ...\n>` +
                   `${result}\n</AgGridReact>`;
        }
        if (this.options.suppressFrameworkContext) {
            // framework context is only hidden if no properties exist for React
            return decreaseIndent(result.trim());
        }
        return `<AgGridReact>${result}\n</AgGridReact>`;
    }

    addComment() {
        return ''; // react comments are added inplace
    }
}

{'fraction_non_alphanumeric': 0.146991310050828, 'fraction_numerical': 0.0018035743564518774, 'mean_word_length': 2.557596967045786, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 37, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5940019', 'n_tokens_mistral': 3419, 'n_tokens_neox': 3366, 'n_words': 712}