Skip to content

Commit

Permalink
refactor: change symbol names and docs to align with similar projects
Browse files Browse the repository at this point in the history
  • Loading branch information
Erick2280 committed Nov 26, 2024
1 parent a49fdcc commit 0188dae
Show file tree
Hide file tree
Showing 40 changed files with 731 additions and 763 deletions.
21 changes: 16 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,31 @@
![Tests workflow status](https://github.com/Erick2280/luisa-deaccessibilizer/actions/workflows/tests.yml/badge.svg)
![Test coverage](https://raw.githubusercontent.com/gist/Erick2280/ae03665a8f65725b43639dde75968a03/raw/badge.svg)

Luisa is a tool designed to intentionally remove accessibility features and degrade SwiftUI components, violating [WCAG 2.2](https://www.w3.org/WAI/WCAG22) guidelines. It helps developers learn how to identify accessibility issues or generate test samples for static accessibility analysis tools.
Luisa is a tool designed to modify codebases by intentionally removing accessibility features and degrading SwiftUI components, violating [WCAG 2.2](https://www.w3.org/WAI/WCAG22) guidelines. It can generate samples for mutation testing and assist in teaching accessibility best practices and how to identify issues.

Luisa code transformation is powered by a set of [fault transformation rules](https://docs.luisa.riso.dev/interfaces/FaultTransformationRule). Each rule targets specific code patterns in `.swift` files and apply a degradation. You can find the source code of each rule, along with the expected behavior and which WCAG success criteria it aims to violate in the [`src/transforming/rules/` folder](./src/transforming/rules/) or in the respective section at the [documentation](https://docs.luisa.riso.dev).
Luisa's code modification is powered by a set of [mutation operators](https://docs.luisa.riso.dev/interfaces/MutationOperator). Each operator targets specific code patterns in `.swift` files and applies a degradation. You can find the source code of each operator, along with its expected behavior and targeted WCAG success it aims to violate, in the `/src/mutating/operators/` folder or in the [documentation](https://docs.luisa.riso.dev).

Luisa uses [tree-sitter](https://tree-sitter.github.io/tree-sitter/) and the [tree-sitter-swift](https://github.com/alex-pinkus/tree-sitter-swift) grammar to generate a concrete syntax tree of a `.swift` source code file. Each fault transformation rule contains a query written in the [tree-sitter query syntax](https://tree-sitter.github.io/tree-sitter/syntax-highlighting#queries) to match specific code patterns, along with instructions on how to modify them.
Luisa uses [tree-sitter](https://tree-sitter.github.io/tree-sitter/) and the [tree-sitter-swift](https://github.com/alex-pinkus/tree-sitter-swift) grammar to generate a syntax tree of a `.swift` source code file. Each mutation operator contains a query written in the [tree-sitter query syntax](https://tree-sitter.github.io/tree-sitter/syntax-highlighting#queries) to match specific code patterns, along with instructions on how to modify them.

## Usage

Luisa can be used as a module for either Node.js and web environments (through the use of WebAssembly), or standalone as a CLI tool.

### Requirements

Luisa supports macOS and Linux machines. To run it locally, you'll need [Node.js LTS](https://nodejs.org) and [emscripten](https://emscripten.org) installed in your machine (to [generate tree-sitter `.wasm` files](https://github.com/tree-sitter/tree-sitter/tree/master/lib/binding_web#generate-wasm-language-files)). If you have [homebrew]() installed, you can install `emscripten` by running `brew install emscripten`.
Luisa supports macOS and Linux machines. To run it locally, you'll need [Node.js LTS](https://nodejs.org) and [emscripten](https://emscripten.org) installed in your machine (to [generate tree-sitter `.wasm` files](https://github.com/tree-sitter/tree-sitter/tree/master/lib/binding_web#generate-wasm-language-files)). If you have [homebrew](https://brew.sh) installed, you can install `emscripten` by running `brew install emscripten`.

If you're running on macOS with Apple silicon, you should also have [Rosetta 2](https://support.apple.com/en-us/102527) installed. You can install it by running `softwareupdate --install-rosetta` in the Terminal.
If you're running on a macOS device with Apple silicon, you should also have [Rosetta 2](https://support.apple.com/en-us/102527) installed. You can install it by running `softwareupdate --install-rosetta` in the Terminal.

### As a module

#### In the browser

If you're installing Luisa to use it as part of a web page, the `tree-sitter.wasm` and `tree-sitter-swift.wasm` files (compiled to `dist/` when you install this module) should be available from the root directory of your web server.

## License

This project is licensed under [MIT License](./LICENSE), excepted where noted otherwise (such as the Swift file samples from external projects).

## Links

Expand All @@ -30,3 +40,4 @@ If you're running on macOS with Apple silicon, you should also have [Rosetta 2](
- [curlconverter](https://github.com/curlconverter/curlconverter)
- [Fruta: Building a Feature-Rich App with SwiftUI - Apple](https://developer.apple.com/documentation/appclip/fruta_building_a_feature-rich_app_with_swiftui)
- [iOS SwiftUI Accessibility Techniques - CVS Health](https://github.com/cvs-health/ios-swiftui-accessibility-techniques)
- [Editing a tree-sitter tree - @jeff-hykin](https://github.com/tree-sitter/tree-sitter/discussions/2553#discussioncomment-9976343)
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "luisa-deaccessibilizer",
"version": "0.0.1",
"description": "A tool to remove accessibility features and degrade SwiftUI components to assist learning and testing.",
"description": "A tool to remove accessibility features and degrade SwiftUI components to assist mutation testing and learning.",
"homepage": "https://docs.luisa.riso.dev",
"bugs": {
"url": "https://github.com/Erick2280/luisa-deaccessibilizer/issues"
Expand Down
81 changes: 36 additions & 45 deletions src/deaccessibilizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@ import Parser, { QueryMatch } from 'web-tree-sitter';
import { SwiftFileTree } from './parsing/swift-file-tree.js';
import { getParser } from './configuring/get-parser.js';
import {
FaultTransformationOptions,
FaultTransformationRule,
} from './transforming/fault-transformation-rule.js';
import { RulesDictionary } from './transforming/rules-dictionary.js';
import { CodeTransformation } from './transforming/code-transformation.js';
MutationGenerationOptions,
MutationOperator,
} from './mutating/mutation-operator.js';
import { OperatorsDictionary } from './mutating/operators-dictionary.js';
import { CodeMutation } from './mutating/code-mutation.js';
import { byNodePosition } from './utils.js';

const allRules = Object.values(RulesDictionary);
const allOperators = Object.values(OperatorsDictionary);

/**
* The main class of the library, responsible for handling Swift files, creating syntax trees,
* performing queries, and transforming code.
* performing queries, and mutating code.
*
* @category Main
*/
Expand Down Expand Up @@ -51,40 +51,35 @@ export class Deaccessibilizer {
}

/**
* Get the code transformations introducing a fault that can be applied to the given tree,
* based on the given rules. If no rules are provided, all rules are considered.
* Get the possible mutations that can be applied to the given tree,
* based on the given operators. If no operators are provided, all of them are considered.
*/
getFaultTransformations(
getCodeMutations(
tree: SwiftFileTree,
rules: FaultTransformationRule[] = allRules,
options: FaultTransformationOptions = {
operators: MutationOperator[] = allOperators,
options: MutationGenerationOptions = {
substituteWithComment: false,
},
): CodeTransformation[] {
return rules.flatMap(
(rule) =>
this.querySwiftUIViews(tree, rule.queryText)
.map((match) => rule.getFaultTransformation(match, options))
.filter(
(transformation) => transformation !== null,
) as CodeTransformation[],
): CodeMutation[] {
return operators.flatMap(
(operator) =>
this.querySwiftUIViews(tree, operator.queryText)
.map((match) => operator.getCodeMutation(match, options))
.filter((mutation) => mutation !== null) as CodeMutation[],
);
}

/**
* Apply the given code transformations to the tree.
* Apply the given mutations to the tree.
*
* This method is faster than {@link applyFaultTransformationsToTreeWithRebuild},
* but it is less safe, since it sorts and applies all transformations at once.
* This method is faster than {@link applyOperatorsToTreeWithRebuild},
* but it is less safe, since it sorts and applies all mutations at once.
*
* This is recommended for small batches of code transformations.
* This is recommended for small batches of mutations.
*/
applyCodeTransformationsToTree(
tree: SwiftFileTree,
codeTransformations: CodeTransformation[],
) {
const nodeChanges = codeTransformations
.flatMap((transformation) => transformation.nodeChanges)
applyCodeMutationsToTree(tree: SwiftFileTree, codeMutations: CodeMutation[]) {
const nodeChanges = codeMutations
.flatMap((mutation) => mutation.nodeChanges)
.sort(byNodePosition)
.reverse();

Expand All @@ -98,28 +93,24 @@ export class Deaccessibilizer {
}

/**
* Get the fault transformations from the given rules and apply them directly to the tree.
* If no rules are provided, all rules are considered.
* Get the mutations from the given operators and apply them directly to the tree.
* If no operators are provided, all of them are considered.
*
* This method is safer than {@link applyCodeTransformationsToTree}, since it applies
* one rule at a time and rebuilds the tree after each transformation.
* This method is safer than {@link applyCodeMutationsToTree}, since it applies
* one operator at a time and rebuilds the tree after each mutation.
*
* This is reccomended for large batches of code transformations.
* This is recommended for large batches of mutations.
*/
applyFaultTransformationsToTreeWithRebuild(
applyOperatorsToTreeWithRebuild(
tree: SwiftFileTree,
rules: FaultTransformationRule[] = allRules,
options: FaultTransformationOptions = {
operators: MutationOperator[] = allOperators,
options: MutationGenerationOptions = {
substituteWithComment: false,
},
) {
for (const rule of rules) {
const transformation = this.getFaultTransformations(
tree,
[rule],
options,
);
this.applyCodeTransformationsToTree(tree, transformation);
for (const operator of operators) {
const mutation = this.getCodeMutations(tree, [operator], options);
this.applyCodeMutationsToTree(tree, mutation);
tree.remountTree();
}
}
Expand Down
33 changes: 14 additions & 19 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,14 @@ export {
SwiftFileTree,
ReplaceNodeOptions,
} from './parsing/swift-file-tree.js';
export { NodeChange, CodeMutation } from './mutating/code-mutation.js';
export {
NodeChange,
CodeTransformation,
} from './transforming/code-transformation.js';
MutationOperator,
MutationGenerationOptions,
} from './mutating/mutation-operator.js';
export {
FaultTransformationRule,
FaultTransformationOptions,
} from './transforming/fault-transformation-rule.js';
export {
RulesDictionary,
RuleId,
OperatorsDictionary,
OperatorId,
AccessibilityElementModifierRemover,
AccessibilityHiddenModifierRemover,
AccessibilityHintModifierRemover,
Expand All @@ -22,19 +19,17 @@ export {
AccessibilityValueModifierRemover,
ImageDecorativeLabelRemover,
StandardFontToStaticSizeReplacer,
} from './transforming/rules-dictionary.js';
} from './mutating/operators-dictionary.js';
export {
buildModifierOnAnyViewQuery,
buildViewWithArgumentLabelQuery,
} from './transforming/builders/query-builders.js';
} from './mutating/builders/query-builders.js';
export {
buildModifierOnAnyViewGTN as buildModifierGTN,
buildFromCaptureNameGTN,
} from './transforming/builders/get-transformable-nodes-builders.js';
buildModifierOnAnyViewFMTN,
buildFromCaptureNameFMTN,
} from './mutating/builders/find-mutation-target-nodes-builders.js';
export {
buildRemoveModifierGFT,
buildRemoveArgumentLabelGFT,
} from './transforming/builders/get-fault-transformation-builders.js';
buildRemoveModifierGCM,
buildRemoveArgumentLabelGCM,
} from './mutating/builders/get-code-mutation-builders.js';
export { TREE_SITTER_BACKEND } from './configuring/get-parser.js';

// TODO: CLI
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import { QueryMatch, SyntaxNode } from 'web-tree-sitter';
import { getModifierArgumentsNodeFromModifierNameNode } from '../../utils.js';

/**
* Builds a {@link FaultTransformationRule.getTransformableNodes} function that returns the node
* Builds a {@link MutationOperator.findMutationTargetNodes} function that returns the node
* of the capture with the given capture name.
*
* @category getTransformableNodes Builders
* @category findMutationTargetNodes Builders
*/
export function buildFromCaptureNameGTN(
export function buildFromCaptureNameFMTN(
captureName: string,
): (match: QueryMatch) => SyntaxNode[] {
return (match) => {
Expand All @@ -18,13 +18,13 @@ export function buildFromCaptureNameGTN(
}

/**
* Builds a {@link FaultTransformationRule.getTransformableNodes} function that, given a match
* Builds a {@link MutationOperator.findMutationTargetNodes} function that, given a match
* that captures the modifier name as `@modifier-name`, returns the nodes of the modifier
* name and its arguments.
*
* @category getTransformableNodes Builders
* @category findMutationTargetNodes Builders
*/
export function buildModifierOnAnyViewGTN(): (
export function buildModifierOnAnyViewFMTN(): (
match: QueryMatch,
) => SyntaxNode[] {
return (match) => {
Expand All @@ -43,10 +43,12 @@ export function buildModifierOnAnyViewGTN(): (
}

/**
* Builds a {@link FaultTransformationRule.getTransformableNodes} function that, given a capture name
* Builds a {@link MutationOperator.findMutationTargetNodes} function that, given a capture name
* and a callback that accepts a node, returns the result of the callback for the captured node.
*
* @category findMutationTargetNodes Builders
*/
export function buildCallbackResultFromCaptureNameGTN(
export function buildCallbackResultFromCaptureNameFMTN(
captureName: string,
callback: (node: SyntaxNode) => SyntaxNode | null,
) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import { QueryMatch, SyntaxNode } from 'web-tree-sitter';
import { CodeTransformation, NodeChange } from '../code-transformation.js';
import { FaultTransformationOptions } from '../fault-transformation-rule.js';
import { CodeMutation, NodeChange } from '../code-mutation.js';
import { MutationGenerationOptions } from '../mutation-operator.js';

/**
* Builds a {@link FaultTransformationRule.getFaultTransformation} that removes a modifier from a view.
* Builds a {@link MutationOperator.getCodeMutation} that removes a modifier from a view.
*
* @category getFaultTransformation Builders
* @category getCodeMutation Builders
*/
export function buildRemoveModifierGFT(
getTransformableNodes: (match: QueryMatch) => SyntaxNode[],
ruleId: string,
export function buildRemoveModifierGCM(
findMutationTargetNodes: (match: QueryMatch) => SyntaxNode[],
operatorId: string,
) {
return (
match: QueryMatch,
options?: FaultTransformationOptions,
): CodeTransformation | null => {
const nodes = getTransformableNodes(match);
options?: MutationGenerationOptions,
): CodeMutation | null => {
const nodes = findMutationTargetNodes(match);

if (nodes.length === 0) {
return null;
Expand All @@ -41,7 +41,7 @@ export function buildRemoveModifierGFT(
};

return {
ruleId,
operatorId,
nodeChanges: nodes
.map((node) => ({
node,
Expand All @@ -57,20 +57,20 @@ export function buildRemoveModifierGFT(
}

/**
* Builds a {@link FaultTransformationRule.getFaultTransformation} that removes the label
* Builds a {@link MutationOperator.getCodeMutation} that removes the label
* from a view argument, keeping only the argument itself.
*
* @category getFaultTransformation Builders
* @category getCodeMutation Builders
*/
export function buildRemoveArgumentLabelGFT(
getTransformableNodes: (match: QueryMatch) => SyntaxNode[],
ruleId: string,
export function buildRemoveArgumentLabelGCM(
findMutationTargetNodes: (match: QueryMatch) => SyntaxNode[],
operatorId: string,
) {
return (
match: QueryMatch,
options?: FaultTransformationOptions,
): CodeTransformation | null => {
const [node] = getTransformableNodes(match);
options?: MutationGenerationOptions,
): CodeMutation | null => {
const [node] = findMutationTargetNodes(match);

if (!node) {
return null;
Expand Down Expand Up @@ -98,29 +98,29 @@ export function buildRemoveArgumentLabelGFT(
].reverse();

return {
ruleId,
operatorId,
nodeChanges,
};
};
}

/**
* Builds a {@link FaultTransformationRule.getFaultTransformation} that replaces the content
* Builds a {@link MutationOperator.getCodeMutation} that replaces the content
* of the node with the result of a callback called with the node as an argument.
* It expects {@link FaultTransformationRule.getTransformableNodes} to return a single node.
* It expects {@link MutationOperator.findMutationTargetNodes} to return a single node.
*/
export function buildReplaceNodeContentWithCallbackResultGFT(
getTransformableNodes: (match: QueryMatch) => SyntaxNode[],
ruleId: string,
export function buildReplaceNodeContentWithCallbackResultGCM(
findMutationTargetNodes: (match: QueryMatch) => SyntaxNode[],
operatorId: string,
builderParams: {
getReplacementTextFromNode: (node: SyntaxNode) => string;
},
) {
return (
match: QueryMatch,
options?: FaultTransformationOptions,
): CodeTransformation | null => {
const [node] = getTransformableNodes(match);
options?: MutationGenerationOptions,
): CodeMutation | null => {
const [node] = findMutationTargetNodes(match);

if (!node) {
return null;
Expand All @@ -129,7 +129,7 @@ export function buildReplaceNodeContentWithCallbackResultGFT(
const replaceWith = builderParams.getReplacementTextFromNode(node);

return {
ruleId,
operatorId,
nodeChanges: [node]
.map((node) => ({
node,
Expand Down
Loading

0 comments on commit 0188dae

Please sign in to comment.